* Commander abilities - fixed that it increases commander tax after cast/play from non-command zone (as example: after Remand effect);

This commit is contained in:
Oleg Agafonov 2019-07-13 10:47:02 +04:00
parent 2197d8ee4a
commit 8c40a1d1a7
15 changed files with 105 additions and 92 deletions

View file

@ -13,18 +13,20 @@ import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.players.Player;
import java.util.Locale;
import java.util.UUID;
/**
* @author Plopman
* @author Plopman, JayDi85
*/
//20130711
// 2019-07-12
/*
* 903.11. If a commander would be put into its owner's graveyard from anywhere, that player may put it into the command zone instead.
* 903.12. If a commander would be put into the exile zone from anywhere, its owner may put it into the command zone instead.
* 903.9. If a commander would be exiled from anywhere or put into its owner's hand, graveyard, or
library from anywhere, its owner may put it into the command zone instead. This replacement effect
may apply more than once to the same event. This is an exception to rule 614.5.
903.9. If a commander would be exiled from anywhere or put into its owners hand, graveyard, or library from anywhere,
its owner may put it into the command zone instead. This replacement effect may apply more than once to the same event.
This is an exception to rule 614.5.
903.9a If a commander is a melded permanent and its owner chooses to put it into the command zone this way,
that permanent and the card representing it that isnt a commander are put into the appropriate zone, and the card
that represents it and is a commander is put into the command zone.
*/
// Oathbreaker mode: If your Oathbreaker changes zones, you may return it to the Command Zone. The Signature Spell must return to the Command Zone.
@ -32,8 +34,8 @@ may apply more than once to the same event. This is an exception to rule 614.5.
public class CommanderReplacementEffect extends ReplacementEffectImpl {
private final UUID commanderId;
private final boolean alsoHand;
private final boolean alsoLibrary;
private final boolean alsoHand; // return from hand to command zone
private final boolean alsoLibrary; // return from library to command zone
private final boolean forceToMove;
private final String commanderTypeName;
@ -87,56 +89,56 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
switch (((ZoneChangeEvent) event).getToZone()) {
case HAND:
if (!alsoHand && ((ZoneChangeEvent) event).getToZone() == Zone.HAND) {
return false;
}
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (!game.isSimulation() && commanderId.equals(zEvent.getTargetId())) {
//System.out.println("applies " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId()));
}
if (zEvent.getToZone().equals(Zone.HAND) && !alsoHand) {
return false;
}
if (zEvent.getToZone().equals(Zone.LIBRARY) && !alsoLibrary) {
return false;
}
// return to command zone
switch (zEvent.getToZone()) {
case LIBRARY:
if (!alsoLibrary && ((ZoneChangeEvent) event).getToZone() == Zone.LIBRARY) {
return false;
}
case HAND:
case GRAVEYARD:
case EXILED:
if (((ZoneChangeEvent) event).getFromZone() == Zone.STACK) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null && commanderId.equals(spell.getSourceId())) {
return true;
}
}
if (commanderId.equals(event.getTargetId())) {
if (commanderId.equals(zEvent.getTargetId())) {
return true;
}
break;
case STACK:
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null) {
if (commanderId.equals(spell.getSourceId())) {
return true;
}
}
break;
}
return false;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) {
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
String originToZone = zEvent.getToZone().toString().toLowerCase(Locale.ENGLISH);
if (!game.isSimulation()) {
//System.out.println("replace " + game.getTurnNum() + ": " + game.getObject(event.getTargetId()).getName() + ": " + zEvent.getFromZone() + " -> " + zEvent.getToZone() + "; " + game.getObject(zEvent.getSourceId()));
}
if (zEvent.getFromZone() == Zone.BATTLEFIELD) {
Permanent permanent = zEvent.getTarget();
if (permanent != null) {
Player player = game.getPlayer(permanent.getOwnerId());
if (player != null && (forceToMove || player.chooseUse(Outcome.Benefit, "Move " + commanderTypeName + " to command zone?", source, game))) {
((ZoneChangeEvent) event).setToZone(Zone.COMMAND);
if (player != null && (forceToMove || player.chooseUse(Outcome.Benefit, "Move " + commanderTypeName + " to command zone instead " + originToZone + "?", source, game))) {
zEvent.setToZone(Zone.COMMAND);
if (!game.isSimulation()) {
game.informPlayers(player.getLogName() + " has moved their " + commanderTypeName + " to the command zone");
game.informPlayers(player.getLogName() + " has moved their " + commanderTypeName + " to the command zone instead " + originToZone);
}
}
}
} else {
Card card = null;
if (((ZoneChangeEvent) event).getFromZone() == Zone.STACK) {
if (zEvent.getFromZone() == Zone.STACK) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null) {
card = game.getCard(spell.getSourceId());
@ -147,10 +149,10 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
}
if (card != null) {
Player player = game.getPlayer(card.getOwnerId());
if (player != null && (forceToMove || player.chooseUse(Outcome.Benefit, "Move " + commanderTypeName + " to command zone?", source, game))) {
if (player != null && (forceToMove || player.chooseUse(Outcome.Benefit, "Move " + commanderTypeName + " to command zone instead " + originToZone + "?", source, game))) {
((ZoneChangeEvent) event).setToZone(Zone.COMMAND);
if (!game.isSimulation()) {
game.informPlayers(player.getLogName() + " has moved their " + commanderTypeName + " to the command zone");
game.informPlayers(player.getLogName() + " has moved their " + commanderTypeName + " to the command zone instead " + originToZone);
}
}
}

View file

@ -20,6 +20,9 @@ import java.util.UUID;
command zone costs an additional {2} for each previous time the player casting it has cast it from
the command zone that game. This additional cost is informally known as the commander tax.
*/
// cast from hand like Remand do not increase commander tax
public class CommanderCostModification extends CostModificationEffectImpl {
private final UUID commanderId;

View file

@ -23,8 +23,11 @@ public abstract class GameCommanderImpl extends GameImpl {
// private final Map<UUID, Cards> mulliganedCards = new HashMap<>();
protected boolean checkCommanderDamage = true;
protected boolean alsoHand; // replace commander going to hand
protected boolean alsoLibrary; // replace commander going to library
// old commander's versions (before 2017) restrict return from hand or library to command zone
protected boolean alsoHand = true; // replace commander going to hand
protected boolean alsoLibrary = true; // replace commander going to library
protected boolean startingPlayerSkipsDraw = true;
public GameCommanderImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) {

View file

@ -1189,11 +1189,17 @@ public abstract class PlayerImpl implements Player, Serializable {
//20091005 - 305.1
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) {
// int bookmark = game.bookmarkState();
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
// land events must return original zone (uses for commander watcher)
Zone cardZoneBefore = game.getState().getZone(card.getId());
GameEvent landEventBefore = GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject());
landEventBefore.setZone(cardZoneBefore);
game.fireEvent(landEventBefore);
if (moveCards(card, Zone.BATTLEFIELD, playLandAbility, game, false, false, false, null)) {
landsPlayed++;
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
GameEvent landEventAfter = GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject());
landEventAfter.setZone(cardZoneBefore);
game.fireEvent(landEventAfter);
game.fireInformEvent(getLogName() + " plays " + card.getLogName());
// game.removeBookmark(bookmark);
resetStoredBookmark(game); // prevent undo after playing a land

View file

@ -1,6 +1,7 @@
package mage.watchers.common;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
@ -12,7 +13,8 @@ import java.util.Map;
import java.util.UUID;
/**
* Calcs commanders play count (spell or land)
* Calcs commanders play count only from command zone (spell or land)
* Cards like Remand can put command to hand and cast it without commander tax increase
*
* @author JayDi85
*/
@ -49,7 +51,7 @@ public class CommanderPlaysCountWatcher extends Watcher {
}
}
if (isCommanderObject) {
if (isCommanderObject && event.getZone() == Zone.COMMAND) {
int count = playsCount.getOrDefault(possibleCommanderId, 0);
count++;
playsCount.put(possibleCommanderId, count);