* Planeswalker abilities - fixed that plus cost's counters is not affected by replacement effects (#5802, example combo: Planeswalker + Pir, Imaginative Rascal + Doubling Season);

This commit is contained in:
Oleg Agafonov 2019-05-17 16:10:34 +04:00
parent 144613eed8
commit ffbd5d373b
14 changed files with 176 additions and 169 deletions

View file

@ -690,27 +690,33 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
sourceId = source.getSourceId();
}
}
GameEvent countersEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTERS, objectId, sourceId, getControllerOrOwner(), counter.getName(), counter.getCount());
countersEvent.setAppliedEffects(appliedEffects);
countersEvent.setFlag(isEffect);
if (!game.replaceEvent(countersEvent)) {
int amount = countersEvent.getAmount();
GameEvent addingAllEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTERS, objectId, sourceId, getControllerOrOwner(), counter.getName(), counter.getCount());
addingAllEvent.setAppliedEffects(appliedEffects);
addingAllEvent.setFlag(isEffect);
if (!game.replaceEvent(addingAllEvent)) {
int amount = addingAllEvent.getAmount();
boolean isEffectFlag = addingAllEvent.getFlag();
int finalAmount = amount;
for (int i = 0; i < amount; i++) {
Counter eventCounter = counter.copy();
eventCounter.remove(eventCounter.getCount() - 1);
GameEvent event = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTER, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1);
event.setAppliedEffects(appliedEffects);
if (!game.replaceEvent(event)) {
GameEvent addingOneEvent = GameEvent.getEvent(GameEvent.EventType.ADD_COUNTER, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1);
addingOneEvent.setAppliedEffects(appliedEffects);
addingOneEvent.setFlag(isEffectFlag);
if (!game.replaceEvent(addingOneEvent)) {
getCounters(game).addCounter(eventCounter);
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1));
GameEvent addedOneEvent = GameEvent.getEvent(GameEvent.EventType.COUNTER_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), 1);
addedOneEvent.setFlag(addingOneEvent.getFlag());
game.fireEvent(addedOneEvent);
} else {
finalAmount--;
returnCode = false;
}
}
if (finalAmount > 0) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERS_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), amount));
GameEvent addedAllEvent = GameEvent.getEvent(GameEvent.EventType.COUNTERS_ADDED, objectId, sourceId, getControllerOrOwner(), counter.getName(), amount);
addedAllEvent.setFlag(isEffectFlag);
game.fireEvent(addedAllEvent);
}
} else {
returnCode = false;

View file

@ -18,6 +18,9 @@ public class GameEvent implements Serializable {
protected UUID sourceId;
protected UUID playerId;
protected int amount;
// flags:
// for counters: event is result of effect (+1 from planeswalkers is cost, not effect)
// for combat damage: event is preventable damage
protected boolean flag;
protected String data;
protected Zone zone;
@ -433,6 +436,17 @@ public class GameEvent implements Serializable {
this.amount = amount;
}
public void setAmountForCounters(int amount, boolean isEffect) {
this.amount = amount;
// cost event must be "transformed" to effect event, as example:
// planeswalker's +1 cost will be affected by Pir, Imaginative Rascal (1 + 1) and applied as effect by Doubling Season (2 * 2)
// https://github.com/magefree/mage/issues/5802
if (isEffect) {
setFlag(true);
}
}
public boolean getFlag() {
return flag;
}

View file

@ -1,10 +1,6 @@
package mage.players;
import com.google.common.collect.ImmutableMap;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import mage.ConditionalMana;
import mage.MageObject;
import mage.MageObjectReference;
@ -70,6 +66,11 @@ import mage.util.GameLog;
import mage.util.RandomUtil;
import org.apache.log4j.Logger;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
public abstract class PlayerImpl implements Player, Serializable {
private static final Logger logger = Logger.getLogger(PlayerImpl.class);
@ -1416,10 +1417,10 @@ public abstract class PlayerImpl implements Player, Serializable {
!= null
// if anyone sees an issue with this code, please report it. Worked in my testing.
|| game.getContinuousEffects().asThough(object.getId(),
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE,
ability,
this.getId(),
game)
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE,
ability,
this.getId(),
game)
!= null) {
if (canUse
|| ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
@ -2009,24 +2010,30 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean addCounters(Counter counter, Game game) {
boolean returnCode = true;
GameEvent countersEvent = GameEvent.getEvent(EventType.ADD_COUNTERS, playerId, null, playerId, counter.getName(), counter.getCount());
if (!game.replaceEvent(countersEvent)) {
int amount = countersEvent.getAmount();
GameEvent addingAllEvent = GameEvent.getEvent(EventType.ADD_COUNTERS, playerId, null, playerId, counter.getName(), counter.getCount());
if (!game.replaceEvent(addingAllEvent)) {
int amount = addingAllEvent.getAmount();
int finalAmount = amount;
boolean isEffectFlag = addingAllEvent.getFlag();
for (int i = 0; i < amount; i++) {
Counter eventCounter = counter.copy();
eventCounter.remove(eventCounter.getCount() - 1);
GameEvent event = GameEvent.getEvent(EventType.ADD_COUNTER, playerId, null, playerId, counter.getName(), 1);
if (!game.replaceEvent(event)) {
GameEvent addingOneEvent = GameEvent.getEvent(EventType.ADD_COUNTER, playerId, null, playerId, counter.getName(), 1);
addingOneEvent.setFlag(isEffectFlag);
if (!game.replaceEvent(addingOneEvent)) {
getCounters().addCounter(eventCounter);
game.fireEvent(GameEvent.getEvent(EventType.COUNTER_ADDED, playerId, null, playerId, counter.getName(), 1));
GameEvent addedOneEvent = GameEvent.getEvent(EventType.COUNTER_ADDED, playerId, null, playerId, counter.getName(), 1);
addedOneEvent.setFlag(addingOneEvent.getFlag());
game.fireEvent(addedOneEvent);
} else {
finalAmount--;
returnCode = false;
}
}
if (finalAmount > 0) {
game.fireEvent(GameEvent.getEvent(EventType.COUNTERS_ADDED, playerId, null, playerId, counter.getName(), amount));
GameEvent addedAllEvent = GameEvent.getEvent(EventType.COUNTERS_ADDED, playerId, null, playerId, counter.getName(), amount);
addedAllEvent.setFlag(addingAllEvent.getFlag());
game.fireEvent(addedAllEvent);
}
} else {
returnCode = false;
@ -2637,7 +2644,7 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* @param game
* @param appliedEffects
* @param numSides Number of sides the dice has
* @param numSides Number of sides the dice has
* @return the number that the player rolled
*/
@Override
@ -2671,10 +2678,10 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* @param game
* @param appliedEffects
* @param numberChaosSides The number of chaos sides the planar die
* currently has (normally 1 but can be 5)
* @param numberChaosSides The number of chaos sides the planar die
* currently has (normally 1 but can be 5)
* @param numberPlanarSides The number of chaos sides the planar die
* currently has (normally 1)
* currently has (normally 1)
* @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
* or NilRoll
*/
@ -2831,7 +2838,7 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* @param ability
* @param available if null, it won't be checked if enough mana is available
* @param available if null, it won't be checked if enough mana is available
* @param sourceObject
* @param game
* @return
@ -3381,7 +3388,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
UUID controllerId, Game game
UUID controllerId, Game game
) {
return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
}
@ -3532,8 +3539,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCards(Card card, Zone toZone,
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) {
Set<Card> cardList = new HashSet<>();
if (card != null) {
@ -3544,22 +3551,22 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCards(Cards cards, Zone toZone,
Ability source, Game game
Ability source, Game game
) {
return moveCards(cards.getCards(game), toZone, source, game);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game
Ability source, Game game
) {
return moveCards(cards, toZone, source, game, false, false, false, null);
}
@Override
public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) {
if (cards.isEmpty()) {
return true;
@ -3645,8 +3652,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardsToExile(Card card, Ability source,
Game game, boolean withName, UUID exileId,
String exileZoneName
Game game, boolean withName, UUID exileId,
String exileZoneName
) {
Set<Card> cards = new HashSet<>();
cards.add(card);
@ -3655,8 +3662,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardsToExile(Set<Card> cards, Ability source,
Game game, boolean withName, UUID exileId,
String exileZoneName
Game game, boolean withName, UUID exileId,
String exileZoneName
) {
if (cards.isEmpty()) {
return true;
@ -3671,14 +3678,14 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
Game game
Game game
) {
return this.moveCardToHandWithInfo(card, sourceId, game, true);
}
@Override
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
Game game, boolean withName
Game game, boolean withName
) {
boolean result = false;
Zone fromZone = game.getState().getZone(card.getId());
@ -3703,7 +3710,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
Game game, Zone fromZone
Game game, Zone fromZone
) {
UUID sourceId = source == null ? null : source.getSourceId();
Set<Card> movedCards = new LinkedHashSet<>();
@ -3711,7 +3718,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// identify cards from one owner
Cards cards = new CardsImpl();
UUID ownerId = null;
for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
Card card = it.next();
if (cards.isEmpty()) {
ownerId = card.getOwnerId();
@ -3772,7 +3779,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
Game game, Zone fromZone
Game game, Zone fromZone
) {
if (card == null) {
return false;
@ -3801,8 +3808,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
Game game, Zone fromZone,
boolean toTop, boolean withName
Game game, Zone fromZone,
boolean toTop, boolean withName
) {
if (card == null) {
return false;
@ -3836,7 +3843,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
Game game, Zone fromZone, boolean withName) {
Game game, Zone fromZone, boolean withName) {
if (card == null) {
return false;
}