* Library of Leng - fixed that it triggers on discarded cards from cost part instead only effect part (#3047);

This commit is contained in:
Oleg Agafonov 2019-07-08 08:31:55 +04:00
parent d6c99437f3
commit 79d40754ed
3 changed files with 57 additions and 41 deletions

View file

@ -21,6 +21,7 @@ public class GameEvent implements Serializable {
// flags:
// for counters: event is result of effect (+1 from planeswalkers is cost, not effect)
// for combat damage: event is preventable damage
// for discard: event is result of effect (1) or result of cost (0)
protected boolean flag;
protected String data;
protected Zone zone;

View file

@ -792,20 +792,23 @@ public abstract class PlayerImpl implements Player, Serializable {
about the discarded card, that cost payment is illegal; the game returns to
the moment before the cost was paid (see rule 717, "Handling Illegal Actions").
*/
if (card != null
&& !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD, card.getId(), source == null ? null : source.getSourceId(), playerId), source)) {
// write info to game log first so game log infos from triggered or replacement effects follow in the game log
if (!game.isSimulation()) {
game.informPlayers(getLogName() + " discards " + card.getLogName());
if (card != null) {
GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD, card.getId(), source == null ? null : source.getSourceId(), playerId);
gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
if (!game.replaceEvent(gameEvent, source)) {
// write info to game log first so game log infos from triggered or replacement effects follow in the game log
if (!game.isSimulation()) {
game.informPlayers(getLogName() + " discards " + card.getLogName());
}
/* If a card is discarded while Rest in Peace is on the battlefield, abilities that function
* when a card is discarded (such as madness) still work, even though that card never reaches
* a graveyard. In addition, spells or abilities that check the characteristics of a discarded
* card (such as Chandra Ablaze's first ability) can find that card in exile. */
card.moveToZone(Zone.GRAVEYARD, source == null ? null : source.getSourceId(), game, false);
// So discard is also successful if card is moved to another zone by replacement effect!
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD, card.getId(), source == null ? null : source.getSourceId(), playerId));
return true;
}
/* If a card is discarded while Rest in Peace is on the battlefield, abilities that function
* when a card is discarded (such as madness) still work, even though that card never reaches
* a graveyard. In addition, spells or abilities that check the characteristics of a discarded
* card (such as Chandra Ablaze's first ability) can find that card in exile. */
card.moveToZone(Zone.GRAVEYARD, source == null ? null : source.getSourceId(), game, false);
// So discard is also successful if card is moved to another zone by replacement effect!
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DISCARDED_CARD, card.getId(), source == null ? null : source.getSourceId(), playerId));
return true;
}
return false;
}
@ -1014,7 +1017,8 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts<ManaCost> manaCosts, Costs<Cost> costs) {
public void setCastSourceIdWithAlternateMana(UUID
sourceId, ManaCosts<ManaCost> manaCosts, Costs<Cost> costs) {
castSourceIdWithAlternateMana = sourceId;
castSourceIdManaCosts = manaCosts;
castSourceIdCosts = costs;
@ -1046,7 +1050,8 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference) {
public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference
reference) {
if (card == null) {
return false;
}
@ -1063,7 +1068,8 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean cast(SpellAbility originalAbility, Game game, boolean noMana, MageObjectReference permittingObject) {
public boolean cast(SpellAbility originalAbility, Game game, boolean noMana, MageObjectReference
permittingObject) {
if (game == null || originalAbility == null) {
return false;
}
@ -1426,7 +1432,8 @@ public abstract class PlayerImpl implements Player, Serializable {
// Get the usable activated abilities for a *single card object*, that is, either a card or half of a split card.
// Also called on the whole split card but only passing the fuse ability and other whole-split-card shared abilities
// as candidates.
private void getUseableActivatedAbilitiesHalfImpl(MageObject object, Zone zone, Game game, Abilities<Ability> candidateAbilites, LinkedHashMap<UUID, ActivatedAbility> output) {
private void getUseableActivatedAbilitiesHalfImpl(MageObject object, Zone zone, Game
game, Abilities<Ability> candidateAbilites, LinkedHashMap<UUID, ActivatedAbility> output) {
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
ManaOptions availableMana = null;
// ManaOptions availableMana = getManaAvailable(game); // can only be activated if mana calculation works flawless otherwise player can't play spells they could play if calculation would work correctly
@ -1496,7 +1503,8 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game
game) {
LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
if (object instanceof StackAbility) { // It may not be possible to activate abilities of stack abilities
return useable;
@ -1515,7 +1523,8 @@ public abstract class PlayerImpl implements Player, Serializable {
}
// Adds special abilities that are given to non permanents by continuous effects
private void getOtherUseableActivatedAbilities(MageObject object, Zone zone, Game game, Map<UUID, ActivatedAbility> useable) {
private void getOtherUseableActivatedAbilities(MageObject object, Zone zone, Game
game, Map<UUID, ActivatedAbility> useable) {
Abilities<ActivatedAbility> otherAbilities = game.getState().getActivatedOtherAbilities(object.getId(), zone);
if (otherAbilities != null) {
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
@ -1561,7 +1570,8 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
protected LinkedHashMap<UUID, ActivatedManaAbilityImpl> getUseableManaAbilities(MageObject object, Zone zone, Game game) {
protected LinkedHashMap<UUID, ActivatedManaAbilityImpl> getUseableManaAbilities(MageObject object, Zone
zone, Game game) {
LinkedHashMap<UUID, ActivatedManaAbilityImpl> useable = new LinkedHashMap<>();
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
for (ActivatedManaAbilityImpl ability : object.getAbilities().getActivatedManaAbilities(zone)) {
@ -1813,7 +1823,9 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
private List<Permanent> getPermanentsThatCanBeUntapped(Game game, List<Permanent> canBeUntapped, RestrictionUntapNotMoreThanEffect handledEffect, Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
private List<Permanent> getPermanentsThatCanBeUntapped(Game
game, List<Permanent> canBeUntapped, RestrictionUntapNotMoreThanEffect
handledEffect, Map<Entry<RestrictionUntapNotMoreThanEffect, Set<Ability>>, Integer> notMoreThanEffectsUsage) {
List<Permanent> leftForUntap = new ArrayList<>();
// select permanents that can still be untapped
for (Permanent permanent : canBeUntapped) {
@ -1996,12 +2008,14 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List<UUID> appliedEffects) {
public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List<
UUID> appliedEffects) {
return doDamage(damage, sourceId, game, combatDamage, preventable, appliedEffects);
}
@SuppressWarnings({"null", "ConstantConditions"})
private int doDamage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List<UUID> appliedEffects) {
private int doDamage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List<
UUID> appliedEffects) {
if (damage > 0) {
if (canDamage(game.getObject(sourceId), game)) {
GameEvent event = new DamagePlayerEvent(playerId, sourceId, playerId, damage, preventable, combatDamage);
@ -2503,7 +2517,8 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId, boolean triggerEvents) {
public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
boolean triggerEvents) {
//20091005 - 701.14c
Library searchedLibrary = null;
String searchInfo = null;
@ -2745,7 +2760,8 @@ public abstract class PlayerImpl implements Player, Serializable {
* or NilRoll
*/
@Override
public PlanarDieRoll rollPlanarDie(Game game, ArrayList<UUID> appliedEffects, int numberChaosSides, int numberPlanarSides) {
public PlanarDieRoll rollPlanarDie(Game game, ArrayList<UUID> appliedEffects, int numberChaosSides,
int numberPlanarSides) {
int result = RandomUtil.nextInt(9) + 1;
PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
if (numberChaosSides + numberPlanarSides > 9) {
@ -2965,7 +2981,8 @@ public abstract class PlayerImpl implements Player, Serializable {
return false;
}
protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions available, Ability ability, Game game) {
protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions available, Ability ability, Game
game) {
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
for (Ability alternateSourceCostsAbility : sourceObject.getAbilities()) {
// if cast for noMana no Alternative costs are allowed
@ -3029,7 +3046,8 @@ public abstract class PlayerImpl implements Player, Serializable {
return false;
}
protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability ability, Game game) {
protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability
ability, Game game) {
if (!(sourceObject instanceof Permanent)) {
Ability sourceAbility = null;
for (Ability landAbility : sourceObject.getAbilities()) {
@ -3064,7 +3082,8 @@ public abstract class PlayerImpl implements Player, Serializable {
return false;
}
private void getPlayableFromGraveyardCard(Game game, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
private void getPlayableFromGraveyardCard(Game game, Card
card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), this.getId(), game);
for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
boolean possible = false;