Merge branch 'master' into refactor/multiple-names

This commit is contained in:
theelk801 2024-10-16 10:38:27 -04:00
commit 45f810f4ed
127 changed files with 2793 additions and 1041 deletions

View file

@ -56,19 +56,12 @@ public class DiesThisOrAnotherTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.isDiesEvent()) {
if (zEvent.getTarget() != null) {
if (!applyFilterOnSource && zEvent.getTarget().getId().equals(this.getSourceId())) {
// TODO: remove this workaround for Basri's Lieutenant
return true;
} else {
if (filter.match(zEvent.getTarget(), getControllerId(), this, game)) {
return true;
}
}
}
if (!zEvent.isDiesEvent() || zEvent.getTarget() == null) {
return false;
}
return false;
// TODO: remove applyFilterOnSource workaround for Basri's Lieutenant
return ((!applyFilterOnSource && zEvent.getTarget().getId().equals(this.getSourceId()))
|| filter.match(zEvent.getTarget(), getControllerId(), this, game));
}
@Override

View file

@ -61,7 +61,7 @@ class EnduringGlimmerReturnEffect extends OneShotEffect {
}
game.addEffect(new EnduringGlimmerTypeEffect()
.setTargetPointer(new FixedTarget(new MageObjectReference(card, game, 1))), source);
return player.moveCards(card, Zone.BATTLEFIELD, source, game);
return player.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null);
}
}

View file

@ -74,8 +74,6 @@ public interface ContinuousEffect extends Effect {
boolean isYourNextEndStep(Game game);
boolean isTheNextEndStep(Game game);
boolean isYourNextUpkeepStep(Game game);
@Override

View file

@ -56,11 +56,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
// until your next turn or until end of your next turn
private UUID startingControllerId; // player to check for turn duration (can't different with real controller ability)
private UUID activePlayerId; // Player whose turn the effect started on
private boolean startingTurnWasActive; // effect started during related players turn and related players turn was already active
private int effectStartingOnTurn = 0; // turn the effect started
private int effectControllerStartingEndStep = 0;
private int effectActivePlayerStartingEndStep = 0;
private int effectStartingEndStep = 0;
private int nextTurnNumber = Integer.MAX_VALUE; // effect is waiting for a step during your next turn, we store it if found.
// set to the turn number on your next turn.
private int effectStartingStepNum = 0; // Some continuous are waiting for the next step of a kind.
@ -95,7 +93,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
this.startingControllerId = effect.startingControllerId;
this.startingTurnWasActive = effect.startingTurnWasActive;
this.effectStartingOnTurn = effect.effectStartingOnTurn;
this.effectControllerStartingEndStep = effect.effectControllerStartingEndStep;
this.effectStartingEndStep = effect.effectStartingEndStep;
this.dependencyTypes = effect.dependencyTypes;
this.dependendToTypes = effect.dependendToTypes;
this.characterDefining = effect.characterDefining;
@ -253,12 +251,10 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
@Override
public void setStartingControllerAndTurnNum(Game game, UUID startingController, UUID activePlayerId) {
this.startingControllerId = startingController;
this.activePlayerId = activePlayerId;
this.startingTurnWasActive = activePlayerId != null
&& activePlayerId.equals(startingController); // you can't use "game" for active player cause it's called from tests/cheat too
this.effectStartingOnTurn = game.getTurnNum();
this.effectControllerStartingEndStep = EndStepCountWatcher.getCount(startingController, game);
this.effectActivePlayerStartingEndStep = EndStepCountWatcher.getCount(activePlayerId, game);
this.effectStartingEndStep = EndStepCountWatcher.getCount(startingController, game);
this.effectStartingStepNum = game.getState().getStepNum();
}
@ -270,12 +266,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
@Override
public boolean isYourNextEndStep(Game game) {
return EndStepCountWatcher.getCount(startingControllerId, game) > effectControllerStartingEndStep;
}
@Override
public boolean isTheNextEndStep(Game game) {
return EndStepCountWatcher.getCount(activePlayerId, game) > effectActivePlayerStartingEndStep;
return EndStepCountWatcher.getCount(startingControllerId, game) > effectStartingEndStep;
}
public boolean isEndCombatOfYourNextTurn(Game game) {
@ -300,14 +291,13 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
@Override
public boolean isInactive(Ability source, Game game) {
// YOUR turn checks
// YOUR turn checks, players who left the game
// until end of turn - must be checked on cleanup step, see rules 514.2
// other must checked here (active and leave players), see rules 800.4
switch (duration) {
case UntilYourNextTurn:
case UntilEndOfYourNextTurn:
case UntilYourNextEndStep:
case UntilTheNextEndStep:
case UntilEndCombatOfYourNextTurn:
case UntilYourNextUpkeepStep:
break;
@ -352,10 +342,6 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
return this.isYourNextEndStep(game);
}
break;
case UntilTheNextEndStep:
if (player != null && player.isInGame()) {
return this.isTheNextEndStep(game);
}
case UntilEndCombatOfYourNextTurn:
if (player != null && player.isInGame()) {
return this.isEndCombatOfYourNextTurn(game);

View file

@ -120,8 +120,8 @@ public class ContinuousEffects implements Serializable {
preventionEffects.removeEndOfCombatEffects();
requirementEffects.removeEndOfCombatEffects();
restrictionEffects.removeEndOfCombatEffects();
for (ContinuousEffectsList asThoughtlist : asThoughEffectsMap.values()) {
asThoughtlist.removeEndOfCombatEffects();
for (ContinuousEffectsList asThoughlist : asThoughEffectsMap.values()) {
asThoughlist.removeEndOfCombatEffects();
}
costModificationEffects.removeEndOfCombatEffects();
spliceCardEffects.removeEndOfCombatEffects();
@ -134,13 +134,27 @@ public class ContinuousEffects implements Serializable {
preventionEffects.removeEndOfTurnEffects(game);
requirementEffects.removeEndOfTurnEffects(game);
restrictionEffects.removeEndOfTurnEffects(game);
for (ContinuousEffectsList asThoughtlist : asThoughEffectsMap.values()) {
asThoughtlist.removeEndOfTurnEffects(game);
for (ContinuousEffectsList asThoughlist : asThoughEffectsMap.values()) {
asThoughlist.removeEndOfTurnEffects(game);
}
costModificationEffects.removeEndOfTurnEffects(game);
spliceCardEffects.removeEndOfTurnEffects(game);
}
public synchronized void removeBeginningOfEndStepEffects(Game game) {
layeredEffects.removeBeginningOfEndStepEffects(game);
continuousRuleModifyingEffects.removeBeginningOfEndStepEffects(game);
replacementEffects.removeBeginningOfEndStepEffects(game);
preventionEffects.removeBeginningOfEndStepEffects(game);
requirementEffects.removeBeginningOfEndStepEffects(game);
restrictionEffects.removeBeginningOfEndStepEffects(game);
for (ContinuousEffectsList asThoughlist : asThoughEffectsMap.values()) {
asThoughlist.removeBeginningOfEndStepEffects(game);
}
costModificationEffects.removeBeginningOfEndStepEffects(game);
spliceCardEffects.removeBeginningOfEndStepEffects(game);
}
public synchronized void removeInactiveEffects(Game game) {
layeredEffects.removeInactiveEffects(game);
continuousRuleModifyingEffects.removeInactiveEffects(game);

View file

@ -71,6 +71,29 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
}
}
public void removeBeginningOfEndStepEffects(Game game) {
// calls every turn on beginning of end step
// rules 514.2
for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
T entry = i.next();
boolean canRemove;
switch (entry.getDuration()) {
case UntilNextEndStep:
canRemove = true;
break;
case UntilYourNextEndStep:
canRemove = entry.isYourNextEndStep(game);
break;
default:
canRemove = false;
}
if (canRemove) {
i.remove();
effectAbilityMap.remove(entry.getId());
}
}
}
public void removeEndOfCombatEffects() {
for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
T entry = i.next();
@ -156,7 +179,6 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
case UntilEndOfYourNextTurn:
case UntilEndCombatOfYourNextTurn:
case UntilYourNextEndStep:
case UntilTheNextEndStep:
case UntilYourNextUpkeepStep:
// until your turn effects continue until real turn reached, their used it's own inactive method
// 514.2 Second, the following actions happen simultaneously: all damage marked on permanents
@ -170,6 +192,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
}
break;
case EndOfTurn:
case UntilNextEndStep:
// end of turn discards on cleanup steps
// 514.2
break;

View file

@ -7,6 +7,8 @@ import mage.constants.Outcome;
import mage.game.Game;
import mage.game.stack.StackObject;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -39,11 +41,15 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget());
if (stackObject != null) {
return stackObject.chooseNewTargets(game, source.getControllerId(), forceChange, onlyOneTarget, null);
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
StackObject stackObject = game.getStack().getStackObject(targetId);
if (stackObject != null) {
stackObject.chooseNewTargets(
game, source.getControllerId(), forceChange, onlyOneTarget, null
);
}
}
return false;
return true;
}
@Override

View file

@ -125,6 +125,10 @@ public class DevourEffect extends ReplacementEffectImpl {
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
String text = "Devour ";
String filterMessage = filterDevoured.getMessage();

View file

@ -32,7 +32,7 @@ public class ExileTopXMayPlayUntilEffect extends OneShotEffect {
makeText(amount.toString().equals("1") ? "that card" : "those cards", duration == Duration.EndOfTurn);
}
private ExileTopXMayPlayUntilEffect(final ExileTopXMayPlayUntilEffect effect) {
protected ExileTopXMayPlayUntilEffect(final ExileTopXMayPlayUntilEffect effect) {
super(effect);
this.amount = effect.amount.copy();
this.duration = effect.duration;
@ -60,10 +60,15 @@ public class ExileTopXMayPlayUntilEffect extends OneShotEffect {
if (!cards.isEmpty()) {
game.addEffect(new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, duration)
.setTargetPointer(new FixedTargets(cards, game)), source);
effectCards(game, source, cards);
}
return true;
}
protected void effectCards(Game game, Ability source, Set<Card> cards) {
//Do nothing, used for derived classes
}
/**
* [Until end of turn, ] you may play [refCardText] [this turn]
*/
@ -76,7 +81,11 @@ public class ExileTopXMayPlayUntilEffect extends OneShotEffect {
String text = "exile the top ";
boolean singular = amount.toString().equals("1");
text += singular ? "card" : CardUtil.numberToText(amount.toString()) + " cards";
text += " of your library. ";
if (amount.toString().equals("X")) {
text += " of your library, where X is " + amount.getMessage() + ". ";
} else {
text += " of your library. ";
}
if (durationRuleAtEnd) {
text += "You may play " + refCardText + ' ' + (duration == Duration.EndOfTurn ? "this turn" : duration.toString());
} else {

View file

@ -26,7 +26,11 @@ public class GoadTargetEffect extends ContinuousEffectImpl {
* each combat if able and attacks a player other than that player if able.
*/
public GoadTargetEffect() {
super(Duration.UntilYourNextTurn, Layer.RulesEffects, SubLayer.NA, Outcome.Detriment);
this(Duration.UntilYourNextTurn);
}
public GoadTargetEffect(Duration duration) {
super(duration, Layer.RulesEffects, SubLayer.NA, Outcome.Detriment);
}
private GoadTargetEffect(final GoadTargetEffect effect) {

View file

@ -251,7 +251,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
String originalObjectInfo = object.toString();
// warning, it's a direct changes to the object (without game state, so no game param here)
object.setName(EmptyNames.FACE_DOWN_CREATURE.toString());
object.setName(EmptyNames.FACE_DOWN_CREATURE.getObjectName());
object.removeAllSuperTypes();
object.getSubtype().clear();
object.removeAllCardTypes();

View file

@ -8,6 +8,7 @@ import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.events.NumberOfTriggersEvent;
import mage.util.CardUtil;
/**
* @author TheElk801
@ -36,15 +37,11 @@ public class AdditionalTriggerControlledETBReplacementEffect extends Replacement
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (!(event instanceof NumberOfTriggersEvent)) {
return false;
}
NumberOfTriggersEvent numberOfTriggersEvent = (NumberOfTriggersEvent) event;
// Only triggers for the source controller
if (!source.isControlledBy(event.getPlayerId())) {
return false;
}
GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent();
GameEvent sourceEvent = ((NumberOfTriggersEvent) event).getSourceEvent();
// Only EtB triggers
if (sourceEvent == null
|| sourceEvent.getType() != GameEvent.EventType.ENTERS_THE_BATTLEFIELD
@ -52,12 +49,12 @@ public class AdditionalTriggerControlledETBReplacementEffect extends Replacement
return false;
}
// Only for triggers of permanents
return game.getPermanent(numberOfTriggersEvent.getSourceId()) != null;
return game.getPermanent(event.getSourceId()) != null;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
event.setAmount(event.getAmount() + 1);
event.setAmount(CardUtil.overflowInc(event.getAmount(), 1));
return false;
}
}

View file

@ -0,0 +1,92 @@
package mage.abilities.effects.common.replacement;
import mage.abilities.Ability;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.util.CardUtil;
/**
* @author notgreat
*/
public class GraveyardFromAnywhereExileReplacementEffect extends ReplacementEffectImpl {
private final FilterCard filter;
private final boolean onlyYou;
private final boolean tokens;
public GraveyardFromAnywhereExileReplacementEffect(FilterCard filter, boolean onlyYou) {
this(Duration.WhileOnBattlefield, filter, onlyYou, false);
}
public GraveyardFromAnywhereExileReplacementEffect(boolean onlyYou, boolean tokens) {
this(Duration.WhileOnBattlefield, StaticFilters.FILTER_CARD_A, onlyYou, tokens);
}
public GraveyardFromAnywhereExileReplacementEffect(Duration duration) {
this(duration, StaticFilters.FILTER_CARD_A, true, false);
}
protected GraveyardFromAnywhereExileReplacementEffect(Duration duration, FilterCard filter, boolean onlyYou, boolean tokens) {
super(duration, Outcome.Exile);
this.filter = filter;
this.onlyYou = onlyYou;
this.tokens = tokens;
this.setText();
}
private GraveyardFromAnywhereExileReplacementEffect(final GraveyardFromAnywhereExileReplacementEffect effect) {
super(effect);
this.filter = effect.filter;
this.onlyYou = effect.onlyYou;
this.tokens = effect.tokens;
}
private void setText() {
this.staticText = "If " + CardUtil.addArticle(filter.getMessage()) + (tokens ? " or token" : "")
+ " would be put into " + (onlyYou ? "your" : "a") + " graveyard from anywhere"
+ (duration == Duration.EndOfTurn ? " this turn" : "") + ", exile "
+ ((filter == StaticFilters.FILTER_CARD_A && !tokens) ? "that card" : "it") + " instead";
}
@Override
public GraveyardFromAnywhereExileReplacementEffect copy() {
return new GraveyardFromAnywhereExileReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.getToZone() != Zone.GRAVEYARD) {
return false;
}
Card card = game.getCard(event.getTargetId());
if (card != null && (!onlyYou || card.isOwnedBy(source.getControllerId())) && (filter == null || filter.match(card, game))) {
return true;
}
Permanent token = game.getPermanent(event.getTargetId());
if (tokens && (token instanceof PermanentToken && (!onlyYou || token.isOwnedBy(source.getControllerId())))) {
return true;
}
return false;
}
}

View file

@ -18,7 +18,10 @@ import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Manifest
@ -79,7 +82,7 @@ public class ManifestEffect extends OneShotEffect {
this(amount, isPlural, false);
}
private ManifestEffect(DynamicValue amount, boolean isPlural, boolean cloakNotManifest) {
public ManifestEffect(DynamicValue amount, boolean isPlural, boolean cloakNotManifest) {
super(Outcome.PutCreatureInPlay);
this.amount = amount;
this.isPlural = isPlural;
@ -115,6 +118,10 @@ public class ManifestEffect extends OneShotEffect {
}
public static List<Permanent> doManifestCards(Game game, Ability source, Player manifestPlayer, Set<Card> cardsToManifest, boolean cloakNotManifest) {
return doManifestCards(game, source, manifestPlayer, cardsToManifest, cloakNotManifest, false);
}
public static List<Permanent> doManifestCards(Game game, Ability source, Player manifestPlayer, Set<Card> cardsToManifest, boolean cloakNotManifest, boolean tapped) {
if (cardsToManifest.isEmpty()) {
return Collections.emptyList();
}
@ -149,7 +156,7 @@ public class ManifestEffect extends OneShotEffect {
List<Permanent> manifested = new ArrayList<>();
// move cards to battlefield as face down
// TODO: possible buggy for multiple cards, see rule 701.34e - it require manifest one by one (card to check: Omarthis, Ghostfire Initiate)
manifestPlayer.moveCards(cardsToManifest, Zone.BATTLEFIELD, source, game, false, true, false, null);
manifestPlayer.moveCards(cardsToManifest, Zone.BATTLEFIELD, source, game, tapped, true, false, null);
for (Card card : cardsToManifest) {
Card battlefieldCard = BecomesFaceDownCreatureEffect.findDefaultCardSideForFaceDown(game, card);

View file

@ -870,7 +870,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
@Override
public String getLogName() {
if (name.isEmpty()) {
return GameLog.getNeutralColoredText(EmptyNames.FACE_DOWN_CREATURE.toString());
return GameLog.getNeutralColoredText(EmptyNames.FACE_DOWN_CREATURE.getObjectName());
} else {
return GameLog.getColoredObjectIdName(this);
}

View file

@ -13,7 +13,7 @@ public enum Duration {
EndOfTurn("until end of turn", true, true),
UntilYourNextTurn("until your next turn", true, true),
UntilYourNextEndStep("until your next end step", true, true),
UntilTheNextEndStep("until your next end step", true, true),
UntilNextEndStep("until the beginning of the next end step", true, true),
UntilEndCombatOfYourNextTurn("until end of combat on your next turn", true, true),
UntilYourNextUpkeepStep("until your next upkeep", true, true),
UntilEndOfYourNextTurn("until the end of your next turn", true, true),

View file

@ -1,33 +1,50 @@
package mage.constants;
import java.util.Arrays;
/**
*
* @author JayDi85
*/
public enum EmptyNames {
// TODO: make names for that cards and enable Assert.assertNotEquals("", permanentName); for assertXXX tests
// TODO: replace all getName().equals to haveSameNames and haveEmptyName
FACE_DOWN_CREATURE(""), // "Face down creature"
FACE_DOWN_TOKEN(""), // "Face down token"
FACE_DOWN_CARD(""); // "Face down card"
FACE_DOWN_CREATURE("", "[face_down_creature]"), // "Face down creature"
FACE_DOWN_TOKEN("", "[face_down_token]"), // "Face down token"
FACE_DOWN_CARD("", "[face_down_card]"); // "Face down card"
public static final String EMPTY_NAME_IN_LOGS = "face down object";
private final String cardName;
private final String objectName; // for mtg rules
private final String testCommand; // for unit tests
EmptyNames(String cardName) {
this.cardName = cardName;
EmptyNames(String objectName, String testCommand) {
this.objectName = objectName;
this.testCommand = testCommand;
}
@Override
public String toString() {
return cardName;
return objectName;
}
/**
* Face down choice for unit tests (use it instead empty string)
*/
public String getTestCommand() {
return this.testCommand;
}
public String getObjectName() {
return this.objectName;
}
public static boolean isEmptyName(String objectName) {
return objectName.equals(FACE_DOWN_CREATURE.toString())
|| objectName.equals(FACE_DOWN_TOKEN.toString())
|| objectName.equals(FACE_DOWN_CARD.toString());
return objectName.equals(FACE_DOWN_CREATURE.getObjectName())
|| objectName.equals(FACE_DOWN_TOKEN.getObjectName())
|| objectName.equals(FACE_DOWN_CARD.getObjectName());
}
public static String replaceTestCommandByObjectName(String searchCommand) {
EmptyNames res = Arrays.stream(values()).filter(e -> e.testCommand.equals(searchCommand)).findAny().orElse(null);
return res == null ? searchCommand : res.objectName;
}
}

View file

@ -19,6 +19,7 @@ import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
import mage.abilities.effects.keyword.FinalityCounterEffect;
import mage.abilities.effects.keyword.ShieldCounterEffect;
import mage.abilities.effects.keyword.StunCounterEffect;
import mage.abilities.hint.common.DayNightHint;
import mage.abilities.keyword.*;
import mage.abilities.mana.DelayedTriggeredManaAbility;
import mage.abilities.mana.TriggeredManaAbility;
@ -1443,6 +1444,9 @@ public abstract class GameImpl implements Game {
getState().addWatcher(bloodthirstWatcher);
}
/**
* Add source of some global effects (as hidden emblems), so users will see good image in stack and logs
*/
public void initGameDefaultHelperEmblems() {
// Rad Counter's trigger source
@ -1456,6 +1460,7 @@ public abstract class GameImpl implements Game {
// global card hints for better UX
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
state.addHelperEmblem(new XmageHelperEmblem().withCardHint("storm counter", StormAbility.getHint()), playerId);
state.addHelperEmblem(new XmageHelperEmblem().withCardHint("day or night", DayNightHint.instance), playerId);
}
}

View file

@ -692,6 +692,11 @@ public class GameState implements Serializable, Copyable<GameState> {
game.applyEffects();
}
// remove beginning of end step effects
public void removeBoESEffects(Game game) {
effects.removeBeginningOfEndStepEffects(game);
}
public void removeTurnStartEffect(Game game) {
delayed.removeStartOfNewTurn(game);
}

View file

@ -248,7 +248,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public String getName() {
if (name.isEmpty()) {
if (faceDown) {
return EmptyNames.FACE_DOWN_CREATURE.toString();
return EmptyNames.FACE_DOWN_CREATURE.getObjectName();
} else {
return "";
}

View file

@ -71,7 +71,7 @@ public class PermanentToken extends PermanentImpl {
@Override
public String getName() {
if (name.isEmpty()) {
return EmptyNames.FACE_DOWN_TOKEN.toString();
return EmptyNames.FACE_DOWN_TOKEN.getObjectName();
} else {
return name;
}

View file

@ -3,8 +3,11 @@
package mage.game.turn;
import mage.constants.PhaseStep;
import mage.game.Game;
import mage.game.events.GameEvent.EventType;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -26,4 +29,10 @@ public class EndStep extends Step {
return new EndStep(this);
}
@Override
public void beginStep(Game game, UUID activePlayerId) {
super.beginStep(game, activePlayerId);
game.getState().removeBoESEffects(game);
}
}

View file

@ -5199,7 +5199,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
visibleName = card.getLogName() + (card.isCopy() ? " (Copy)" : "");
} else {
visibleName = "a " + GameLog.getNeutralObjectIdName(EmptyNames.FACE_DOWN_CARD.toString(), card.getId());
visibleName = "a " + GameLog.getNeutralObjectIdName(EmptyNames.FACE_DOWN_CARD.getObjectName(), card.getId());
}
game.informPlayers(this.getLogName() + " moves " + visibleName
+ (fromZone != null ? " from " + fromZone.toString().toLowerCase(Locale.ENGLISH) : "")