Update effects and abilities which involve tapping permanents for mana (#7679)

Abilities which use trigger from or replace tapping for mana now save the permanent in case it leaves the battlefield.
fixes #7671, fixes #7770
* Merge fix and additional comments, Winter's Night fixed

Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
Evan Kranzler 2021-07-21 21:09:07 -04:00 committed by GitHub
parent 81193148e9
commit 214b688fdb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 884 additions and 1149 deletions

View file

@ -8,11 +8,12 @@ import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ManaEvent;
import mage.game.events.TappedForManaEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
* Non mana triggered ability (use case: you must apply non mana effects on mana taps like gain life)
*
* @author LevelX2
*/
@ -41,26 +42,26 @@ public class TapForManaAllTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA
// it's non mana triggered ability, so ignore it on checking, see TAPPED_FOR_MANA
if (game.inCheckPlayableState()) {
return false;
}
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (filter.match(permanent, getSourceId(), getControllerId(), game)) {
ManaEvent mEvent = (ManaEvent) event;
for(Effect effect:getEffects()) {
effect.setValue("mana", mEvent.getMana());
}
switch(setTargetPointer) {
case PERMANENT:
getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game));
break;
case PLAYER:
getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
}
return true;
TappedForManaEvent manaEvent = ((TappedForManaEvent) event);
Permanent permanent = manaEvent.getPermanent();
if (permanent == null || !filter.match(permanent, getSourceId(), getControllerId(), game)) {
return false;
}
return false;
getEffects().setValue("mana", manaEvent.getMana());
getEffects().setValue("tappedPermanent", permanent);
switch (setTargetPointer) {
case PERMANENT:
getEffects().setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game)));
break;
case PLAYER:
getEffects().setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
}
return true;
}
@Override

View file

@ -1,7 +1,5 @@
package mage.abilities.common;
import mage.abilities.effects.Effect;
import mage.abilities.effects.mana.ManaEffect;
import mage.abilities.mana.TriggeredManaAbility;
import mage.constants.SetTargetPointer;
@ -9,15 +7,15 @@ import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ManaEvent;
import mage.game.events.TappedForManaEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
* Mana triggered ability (use case: you must produce new mana on mana taps)
*
* @author LevelX2
*/
public class TapForManaAllTriggeredManaAbility extends TriggeredManaAbility {
private final FilterPermanent filter;
@ -42,23 +40,22 @@ public class TapForManaAllTriggeredManaAbility extends TriggeredManaAbility {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (filter.match(permanent, getSourceId(), getControllerId(), game)) {
ManaEvent mEvent = (ManaEvent) event;
for(Effect effect:getEffects()) {
effect.setValue("mana", mEvent.getMana());
switch(setTargetPointer) {
case PERMANENT:
effect.setTargetPointer(new FixedTarget(permanent, game));
break;
case PLAYER:
effect.setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
}
}
return true;
TappedForManaEvent manaEvent = ((TappedForManaEvent) event);
Permanent permanent = manaEvent.getPermanent();
if (permanent == null || !filter.match(permanent, getSourceId(), getControllerId(), game)) {
return false;
}
return false;
getEffects().setValue("mana", manaEvent.getMana());
getEffects().setValue("tappedPermanent", permanent);
switch (setTargetPointer) {
case PERMANENT:
getEffects().setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game)));
break;
case PLAYER:
getEffects().setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
}
return true;
}
@Override

View file

@ -1,69 +0,0 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author Quercitron
*/
public class TapLandForManaAllTriggeredAbility extends TriggeredAbilityImpl {
private final boolean setTargetPointer;
private final boolean landMustExists;
public TapLandForManaAllTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer, boolean landMustExists) {
super(Zone.BATTLEFIELD, effect, optional);
this.setTargetPointer = setTargetPointer;
this.landMustExists = landMustExists;
}
public TapLandForManaAllTriggeredAbility(final TapLandForManaAllTriggeredAbility ability) {
super(ability);
this.setTargetPointer = ability.setTargetPointer;
this.landMustExists = ability.landMustExists;
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TAPPED_FOR_MANA;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA
return false;
}
Permanent permanent;
if (landMustExists) {
permanent = game.getPermanent(event.getSourceId());
} else {
permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
}
if (permanent != null && permanent.isLand(game)) {
if (setTargetPointer) {
getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game));
}
return true;
}
return false;
}
@Override
public TapLandForManaAllTriggeredAbility copy() {
return new TapLandForManaAllTriggeredAbility(this);
}
@Override
public String getTriggerPhrase() {
return "Whenever a land is tapped for mana, ";
}
}

View file

@ -1,57 +0,0 @@
package mage.abilities.common;
import mage.abilities.effects.mana.ManaEffect;
import mage.abilities.mana.TriggeredManaAbility;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author LevelX2
*/
public class TapLandForManaAllTriggeredManaAbility extends TriggeredManaAbility {
private final boolean setTargetPointer;
public TapLandForManaAllTriggeredManaAbility(ManaEffect manaEffect, boolean optional, boolean setTargetPointer) {
super(Zone.BATTLEFIELD, manaEffect, optional);
this.setTargetPointer = setTargetPointer;
}
public TapLandForManaAllTriggeredManaAbility(final TapLandForManaAllTriggeredManaAbility ability) {
super(ability);
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TAPPED_FOR_MANA;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (permanent != null && permanent.isLand(game)) {
if (setTargetPointer) {
getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game));
}
return true;
}
return false;
}
@Override
public TapLandForManaAllTriggeredManaAbility copy() {
return new TapLandForManaAllTriggeredManaAbility(this);
}
@Override
public String getTriggerPhrase() {
return "Whenever a player taps a land for mana, ";
}
}

View file

@ -27,7 +27,7 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
@Override
public Player getPlayer(Game game, Ability source) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
Permanent permanent = (Permanent) getValue("tappedPermanent");
if (permanent != null) {
return game.getPlayer(permanent.getControllerId());
}
@ -38,25 +38,26 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
public List<Mana> getNetMana(Game game, Ability source) {
List<Mana> netMana = new ArrayList<>();
Mana types = (Mana) this.getValue("mana");
if (types != null) {
if (types.getBlack() > 0) {
netMana.add(Mana.BlackMana(1));
}
if (types.getRed() > 0) {
netMana.add(Mana.RedMana(1));
}
if (types.getBlue() > 0) {
netMana.add(Mana.BlueMana(1));
}
if (types.getGreen() > 0) {
netMana.add(Mana.GreenMana(1));
}
if (types.getWhite() > 0) {
netMana.add(Mana.WhiteMana(1));
}
if (types.getColorless() > 0) {
netMana.add(Mana.ColorlessMana(1));
}
if (types == null) {
return netMana;
}
if (types.getBlack() > 0) {
netMana.add(Mana.BlackMana(1));
}
if (types.getRed() > 0) {
netMana.add(Mana.RedMana(1));
}
if (types.getBlue() > 0) {
netMana.add(Mana.BlueMana(1));
}
if (types.getGreen() > 0) {
netMana.add(Mana.GreenMana(1));
}
if (types.getWhite() > 0) {
netMana.add(Mana.WhiteMana(1));
}
if (types.getColorless() > 0) {
netMana.add(Mana.ColorlessMana(1));
}
return netMana;
}
@ -64,68 +65,69 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
@Override
public Mana produceMana(Game game, Ability source) {
Mana newMana = new Mana();
if (game != null) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (permanent != null) {
Player targetController = game.getPlayer(permanent.getControllerId());
Mana types = (Mana) this.getValue("mana");
if (targetController == null || types == null) {
return newMana;
}
if (game == null) {
return newMana;
}
Permanent permanent = (Permanent) this.getValue("tappedPermanent");
Mana types = (Mana) this.getValue("mana");
if (permanent == null || types == null) {
return newMana;
}
Player targetController = game.getPlayer(permanent.getControllerId());
if (targetController == null) {
return newMana;
}
Choice choice = new ChoiceColor(true);
choice.getChoices().clear();
choice.setMessage("Pick the type of mana to produce");
if (types.getBlack() > 0) {
choice.getChoices().add("Black");
}
if (types.getRed() > 0) {
choice.getChoices().add("Red");
}
if (types.getBlue() > 0) {
choice.getChoices().add("Blue");
}
if (types.getGreen() > 0) {
choice.getChoices().add("Green");
}
if (types.getWhite() > 0) {
choice.getChoices().add("White");
}
if (types.getColorless() > 0) {
choice.getChoices().add("Colorless");
}
Choice choice = new ChoiceColor(true);
choice.getChoices().clear();
choice.setMessage("Pick the type of mana to produce");
if (types.getWhite() > 0) {
choice.getChoices().add("White");
}
if (types.getBlue() > 0) {
choice.getChoices().add("Blue");
}
if (types.getBlack() > 0) {
choice.getChoices().add("Black");
}
if (types.getRed() > 0) {
choice.getChoices().add("Red");
}
if (types.getGreen() > 0) {
choice.getChoices().add("Green");
}
if (types.getColorless() > 0) {
choice.getChoices().add("Colorless");
}
if (!choice.getChoices().isEmpty()) {
if (choice.getChoices().size() == 1) {
choice.setChoice(choice.getChoices().iterator().next());
} else {
if (!targetController.choose(outcome, choice, game)) {
return newMana;
}
}
if (choice.getChoices().isEmpty()) {
return newMana;
}
if (choice.getChoices().size() != 1
&& !targetController.choose(outcome, choice, game)) {
return newMana;
}
choice.setChoice(choice.getChoices().iterator().next());
switch (choice.getChoice()) {
case "Black":
newMana.setBlack(1);
break;
case "Blue":
newMana.setBlue(1);
break;
case "Red":
newMana.setRed(1);
break;
case "Green":
newMana.setGreen(1);
break;
case "White":
newMana.setWhite(1);
break;
case "Colorless":
newMana.setColorless(1);
break;
}
}
}
switch (choice.getChoice()) {
case "White":
newMana.setWhite(1);
break;
case "Blue":
newMana.setBlue(1);
break;
case "Black":
newMana.setBlack(1);
break;
case "Red":
newMana.setRed(1);
break;
case "Green":
newMana.setGreen(1);
break;
case "Colorless":
newMana.setColorless(1);
break;
}
return newMana;
}
@ -134,5 +136,4 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
public AddManaOfAnyTypeProducedEffect copy() {
return new AddManaOfAnyTypeProducedEffect(this);
}
}

View file

@ -8,8 +8,8 @@ import mage.constants.AbilityType;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ManaEvent;
import mage.game.events.TappedForManaEvent;
import mage.players.Player;
import java.util.ArrayList;
@ -133,7 +133,7 @@ public abstract class ManaEffect extends OneShotEffect {
*/
public void checkToFirePossibleEvents(Mana mana, Game game, Ability source) {
if (source.getAbilityType() == AbilityType.MANA && source.hasTapCost()) {
ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, source.getSourceId(), source, source.getControllerId(), mana);
ManaEvent event = new TappedForManaEvent(source.getSourceId(), source, source.getControllerId(), mana, game);
if (!game.replaceEvent(event)) {
game.fireEvent(event);
}

View file

@ -1,19 +1,16 @@
package mage.abilities.mana;
import mage.ConditionalMana;
import mage.Mana;
import mage.abilities.Ability;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ManaEvent;
import mage.game.events.TappedForManaEvent;
import mage.players.Player;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import mage.ConditionalMana;
import java.util.*;
/**
* @author BetaSteward_at_googlemail.com
@ -128,7 +125,7 @@ public class ManaOptions extends ArrayList<Mana> {
*/
private boolean checkManaReplacementAndTriggeredMana(Ability ability, Game game, Mana mana) {
if (ability.hasTapCost()) {
ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, ability.getSourceId(), ability, ability.getControllerId(), mana);
ManaEvent event = new TappedForManaEvent(ability.getSourceId(), ability, ability.getControllerId(), mana, game);
if (game.replaceEvent(event)) {
return false;
}
@ -415,7 +412,7 @@ public class ManaOptions extends ArrayList<Mana> {
if (manaToAdd != null && (manaToAdd.countColored() > 0 || manaToAdd.getAny() > 0) && manaToAdd.count() > 0 && onlyManaCosts) {
repeatable = true; // only replace to any with mana costs only will be repeated if able
}
for (Mana payCombination : ManaOptions.getPossiblePayCombinations(cost, currentMana)) {
Mana currentManaCopy = currentMana.copy(); // copy start mana because in iteration it will be updated
while (currentManaCopy.includesMana(payCombination)) { // loop for multiple usage if possible
@ -553,7 +550,7 @@ public class ManaOptions extends ArrayList<Mana> {
public boolean removeEqualMana(Mana manaToRemove) {
boolean result = false;
for (Iterator<Mana> iterator = this.iterator(); iterator.hasNext();) {
for (Iterator<Mana> iterator = this.iterator(); iterator.hasNext(); ) {
Mana next = iterator.next();
if (next.equalManaValue(manaToRemove)) {
iterator.remove();
@ -630,7 +627,7 @@ public class ManaOptions extends ArrayList<Mana> {
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
for (; ; ) {
Mana mana = it.next();
sb.append(mana.toString());
if (mana instanceof ConditionalMana) {

View file

@ -0,0 +1,31 @@
package mage.filter.predicate.mageobject;
import mage.MageObject;
import mage.ObjectColor;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
/**
* @author TheElk801
*/
public enum ChosenColorPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<MageObject>> {
TRUE(true), FALSE(false);
private final boolean value;
ChosenColorPredicate(boolean value) {
this.value = value;
}
@Override
public boolean apply(ObjectSourcePlayer<MageObject> input, Game game) {
ObjectColor color = (ObjectColor) game.getState().getValue(input.getSourceId() + "_color");
return color != null && input.getObject().getColor(game).shares(color) == value;
}
@Override
public String toString() {
return "Chosen subtype";
}
}

View file

@ -1,13 +1,11 @@
package mage.game.events;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class ManaEvent extends GameEvent {
@ -22,5 +20,4 @@ public class ManaEvent extends GameEvent {
public Mana getMana() {
return mana;
}
}

View file

@ -0,0 +1,25 @@
package mage.game.events;
import mage.Mana;
import mage.abilities.Ability;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public class TappedForManaEvent extends ManaEvent {
private final Permanent permanent;
public TappedForManaEvent(UUID targetId, Ability source, UUID playerId, Mana mana, Game game) {
super(EventType.TAPPED_FOR_MANA, targetId, source, playerId, mana);
this.permanent = source.getSourcePermanentOrLKI(game);
}
public Permanent getPermanent() {
return permanent;
}
}