mirror of
https://github.com/magefree/mage.git
synced 2026-01-23 03:39:54 -08:00
Merge branch 'master' into refactor/multiple-names
This commit is contained in:
commit
45f810f4ed
127 changed files with 2793 additions and 1041 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,8 +74,6 @@ public interface ContinuousEffect extends Effect {
|
|||
|
||||
boolean isYourNextEndStep(Game game);
|
||||
|
||||
boolean isTheNextEndStep(Game game);
|
||||
|
||||
boolean isYourNextUpkeepStep(Game game);
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) : "")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue