reworked AttacksWithCreaturesTriggeredAbility implementation and text generation, fixed issues where it could trigger when nothing attacked

This commit is contained in:
Evan Kranzler 2021-08-30 19:51:46 -04:00
parent 1843459e84
commit 600b4fe110
12 changed files with 49 additions and 78 deletions

View file

@ -47,7 +47,7 @@ public final class AlibouAncientWitness extends CardImpl {
Ability ability = new AttacksWithCreaturesTriggeredAbility( Ability ability = new AttacksWithCreaturesTriggeredAbility(
new AlibouAncientWitnessEffect(), 1, new AlibouAncientWitnessEffect(), 1,
StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE
); ).setTriggerPhrase("Whenever one or more artifact creatures you control attack, ");
ability.addTarget(new TargetAnyTarget()); ability.addTarget(new TargetAnyTarget());
this.addAbility(ability.addHint(AlibouAncientWitnessEffect.getHint())); this.addAbility(ability.addHint(AlibouAncientWitnessEffect.getHint()));
} }

View file

@ -1,20 +1,20 @@
package mage.cards.a; package mage.cards.a;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.constants.SubType;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubType;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import java.util.UUID;
/** /**
* @author TheElk801 * @author TheElk801
*/ */
@ -31,7 +31,9 @@ public final class AncestorDragon extends CardImpl {
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// Whenever one or more creatures you control attack, you gain 1 life for each attacking creature. // Whenever one or more creatures you control attack, you gain 1 life for each attacking creature.
this.addAbility(new AttacksWithCreaturesTriggeredAbility(new AncestorDragonEffect(), 1)); this.addAbility(new AttacksWithCreaturesTriggeredAbility(
new AncestorDragonEffect(), 1
).setTriggerPhrase("Whenever one or more creatures you control attack, "));
} }
private AncestorDragon(final AncestorDragon card) { private AncestorDragon(final AncestorDragon card) {

View file

@ -37,7 +37,9 @@ public final class AngelicGuardian extends CardImpl {
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// Whenever one or more creatures you control attack, they gain indestructible until end of turn // Whenever one or more creatures you control attack, they gain indestructible until end of turn
this.addAbility(new AttacksWithCreaturesTriggeredAbility(new AngelicGuardianGainEffect(), 1)); this.addAbility(new AttacksWithCreaturesTriggeredAbility(
new AngelicGuardianGainEffect(), 1
).setTriggerPhrase("Whenever one or more creatures you control attack, "));
} }
private AngelicGuardian(final AngelicGuardian card) { private AngelicGuardian(final AngelicGuardian card) {

View file

@ -47,7 +47,7 @@ public final class KrydleOfBaldursGate extends CardImpl {
// Whenever you attack, you may pay {2}. If you do, target creature can't be blocked this turn. // Whenever you attack, you may pay {2}. If you do, target creature can't be blocked this turn.
ability = new AttacksWithCreaturesTriggeredAbility(new DoIfCostPaid( ability = new AttacksWithCreaturesTriggeredAbility(new DoIfCostPaid(
new CantBeBlockedTargetEffect(), new GenericManaCost(2) new CantBeBlockedTargetEffect(), new GenericManaCost(2)
), 0); ), 1);
ability.addTarget(new TargetCreaturePermanent()); ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -68,7 +68,7 @@ public final class PaladinClass extends CardImpl {
new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn, true) new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn, true)
.setText("until end of turn, target attacking creature " + .setText("until end of turn, target attacking creature " +
"gets +1/+1 for each other attacking creature"), "gets +1/+1 for each other attacking creature"),
0 1
); );
ability.addEffect(new GainAbilityTargetEffect( ability.addEffect(new GainAbilityTargetEffect(
DoubleStrikeAbility.getInstance(), Duration.EndOfTurn DoubleStrikeAbility.getInstance(), Duration.EndOfTurn

View file

@ -1,25 +1,25 @@
package mage.cards.p; package mage.cards.p;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition; import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.AttackingCreatureCount;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController; import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import java.util.UUID;
/** /**
* @author jeffwadsworth * @author jeffwadsworth
*/ */
@ -32,16 +32,20 @@ public final class PathOfBravery extends CardImpl {
} }
static final String rule = "As long as your life total is greater than or equal to your starting life total, creatures you control get +1/+1"; static final String rule = "As long as your life total is greater than or equal to your starting life total, creatures you control get +1/+1";
private static final DynamicValue xValue = new AttackingCreatureCount();
public PathOfBravery(UUID ownerId, CardSetInfo setInfo) { public PathOfBravery(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
// As long as your life total is greater than or equal to your starting life total, creatures you control get +1/+1. // As long as your life total is greater than or equal to your starting life total, creatures you control get +1/+1.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, true), LifeCondition.instance, rule))); this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(new BoostAllEffect(
1, 1, Duration.WhileOnBattlefield, filter, true
), LifeCondition.instance, rule)));
// Whenever one or more creatures you control attack, you gain life equal to the number of attacking creatures. // Whenever one or more creatures you control attack, you gain life equal to the number of attacking creatures.
this.addAbility(new AttacksWithCreaturesTriggeredAbility(new PathOfBraveryEffect(), 1)); this.addAbility(new AttacksWithCreaturesTriggeredAbility(new GainLifeEffect(
xValue, "you gain life equal to the number of attacking creatures"
), 1).setTriggerPhrase("Whenever one or more creatures you control attack, "));
} }
private PathOfBravery(final PathOfBravery card) { private PathOfBravery(final PathOfBravery card) {
@ -67,34 +71,3 @@ enum LifeCondition implements Condition {
return false; return false;
} }
} }
class PathOfBraveryEffect extends OneShotEffect {
private int attackers;
public PathOfBraveryEffect() {
super(Outcome.GainLife);
staticText = "you gain life equal to the number of attacking creatures";
}
public PathOfBraveryEffect(final PathOfBraveryEffect effect) {
super(effect);
}
@Override
public PathOfBraveryEffect copy() {
return new PathOfBraveryEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player you = game.getPlayer(source.getControllerId());
attackers = game.getCombat().getAttackers().size();
if (you != null) {
you.gainLife(attackers, game, source);
attackers = 0;
return true;
}
return false;
}
}

View file

@ -82,7 +82,7 @@ public final class PlarggDeanOfChaos extends ModalDoubleFacesCard {
// Whenever you attack, untap each creature you control, then tap any number of creatures you control. // Whenever you attack, untap each creature you control, then tap any number of creatures you control.
AttacksWithCreaturesTriggeredAbility augustaAbility = new AttacksWithCreaturesTriggeredAbility( AttacksWithCreaturesTriggeredAbility augustaAbility = new AttacksWithCreaturesTriggeredAbility(
new UntapAllControllerEffect(StaticFilters.FILTER_PERMANENT_CREATURES, "untap each creature you control"), 0); new UntapAllControllerEffect(StaticFilters.FILTER_PERMANENT_CREATURES, "untap each creature you control"), 1);
augustaAbility.addEffect(new AugustaDeanOfOrderEffect().concatBy(", then")); augustaAbility.addEffect(new AugustaDeanOfOrderEffect().concatBy(", then"));
this.getRightHalfCard().addAbility(augustaAbility); this.getRightHalfCard().addAbility(augustaAbility);
} }

View file

@ -1,4 +1,3 @@
package mage.cards.r; package mage.cards.r;
import mage.MageObject; import mage.MageObject;
@ -33,7 +32,6 @@ import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
*
* @author L_J * @author L_J
*/ */
public final class RagingRiver extends CardImpl { public final class RagingRiver extends CardImpl {
@ -42,7 +40,9 @@ public final class RagingRiver extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{R}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{R}");
// Whenever one or more creatures you control attack, each defending player divides all creatures without flying they control into a "left" pile and a "right" pile. Then, for each attacking creature you control, choose "left" or "right." That creature can't be blocked this combat except by creatures with flying and creatures in a pile with the chosen label. // Whenever one or more creatures you control attack, each defending player divides all creatures without flying they control into a "left" pile and a "right" pile. Then, for each attacking creature you control, choose "left" or "right." That creature can't be blocked this combat except by creatures with flying and creatures in a pile with the chosen label.
this.addAbility(new AttacksWithCreaturesTriggeredAbility(new RagingRiverEffect(), 1)); this.addAbility(new AttacksWithCreaturesTriggeredAbility(
new RagingRiverEffect(), 1
).setTriggerPhrase("Whenever one or more creatures you control attack, "));
} }
private RagingRiver(final RagingRiver card) { private RagingRiver(final RagingRiver card) {
@ -92,20 +92,19 @@ class RagingRiverEffect extends OneShotEffect {
if (target.getTargets().contains(permanent.getId())) { if (target.getTargets().contains(permanent.getId())) {
left.add(permanent); left.add(permanent);
leftLog.add(permanent); leftLog.add(permanent);
} } else if (filterBlockers.match(permanent, source.getSourceId(), defenderId, game)) {
else if (filterBlockers.match(permanent, source.getSourceId(), defenderId, game)) {
right.add(permanent); right.add(permanent);
rightLog.add(permanent); rightLog.add(permanent);
} }
} }
} }
// it could be nice to invoke some graphic indicator of which creature is Left or Right in this spot // it could be nice to invoke some graphic indicator of which creature is Left or Right in this spot
StringBuilder sb = new StringBuilder("Left pile of ").append(defender.getLogName()).append(": "); StringBuilder sb = new StringBuilder("Left pile of ").append(defender.getLogName()).append(": ");
sb.append(leftLog.stream().map(MageObject::getLogName).collect(Collectors.joining(", "))); sb.append(leftLog.stream().map(MageObject::getLogName).collect(Collectors.joining(", ")));
game.informPlayers(sb.toString()); game.informPlayers(sb.toString());
sb = new StringBuilder("Right pile of ").append(defender.getLogName()).append(": "); sb = new StringBuilder("Right pile of ").append(defender.getLogName()).append(": ");
sb.append(rightLog.stream().map(MageObject::getLogName).collect(Collectors.joining(", "))); sb.append(rightLog.stream().map(MageObject::getLogName).collect(Collectors.joining(", ")));
@ -135,7 +134,7 @@ class RagingRiverEffect extends OneShotEffect {
.filter(permanent -> permanent.isControlledBy(defender.getId())) .filter(permanent -> permanent.isControlledBy(defender.getId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
if (controller.choosePile(outcome, attacker.getName() + ": attacking " + defender.getName(), leftLog, rightLog, game)) { if (controller.choosePile(outcome, attacker.getName() + ": attacking " + defender.getName(), leftLog, rightLog, game)) {
filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentInListPredicate(left)))); filter.add(Predicates.not(Predicates.or(new AbilityPredicate(FlyingAbility.class), new PermanentInListPredicate(left))));
game.informPlayers(attacker.getLogName() + ": attacks left (" + defender.getLogName() + ")"); game.informPlayers(attacker.getLogName() + ": attacks left (" + defender.getLogName() + ")");

View file

@ -47,7 +47,7 @@ public final class RangerClass extends CardImpl {
// Whenever you attack, put a +1/+1 counter on target attacking creature. // Whenever you attack, put a +1/+1 counter on target attacking creature.
Ability ability = new AttacksWithCreaturesTriggeredAbility( Ability ability = new AttacksWithCreaturesTriggeredAbility(
new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 0 new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 1
); );
ability.addTarget(new TargetAttackingCreature()); ability.addTarget(new TargetAttackingCreature());
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 2))); this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 2)));

View file

@ -29,7 +29,7 @@ public final class SparringRegimen extends CardImpl {
// Whenever you attack, put a +1/+1 counter on target attacking creature and untap it. // Whenever you attack, put a +1/+1 counter on target attacking creature and untap it.
Ability ability = new AttacksWithCreaturesTriggeredAbility( Ability ability = new AttacksWithCreaturesTriggeredAbility(
new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 0 new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 1
); );
ability.addEffect(new UntapTargetEffect().setText("and untap it")); ability.addEffect(new UntapTargetEffect().setText("and untap it"));
ability.addTarget(new TargetAttackingCreature()); ability.addTarget(new TargetAttackingCreature());

View file

@ -23,7 +23,7 @@ public final class ThoroughInvestigation extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
// Whenever you attack, investigate. // Whenever you attack, investigate.
this.addAbility(new AttacksWithCreaturesTriggeredAbility(new InvestigateEffect(), 0)); this.addAbility(new AttacksWithCreaturesTriggeredAbility(new InvestigateEffect(), 1));
// Whenever you sacrifice a Clue, venture into the dungeon. // Whenever you sacrifice a Clue, venture into the dungeon.
this.addAbility(new SacrificePermanentTriggeredAbility(new VentureIntoTheDungeonEffect(), filter)); this.addAbility(new SacrificePermanentTriggeredAbility(new VentureIntoTheDungeonEffect(), filter));

View file

@ -7,18 +7,15 @@ import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.util.CardUtil; import mage.util.CardUtil;
import java.util.UUID;
/** /**
* @author Styxo * @author Styxo
*/ */
public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl { public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl {
private FilterCreaturePermanent filter; private final FilterCreaturePermanent filter;
private int minAttackers; private final int minAttackers;
public AttacksWithCreaturesTriggeredAbility(Effect effect, int minAttackers) { public AttacksWithCreaturesTriggeredAbility(Effect effect, int minAttackers) {
this(effect, minAttackers, StaticFilters.FILTER_PERMANENT_CREATURES); this(effect, minAttackers, StaticFilters.FILTER_PERMANENT_CREATURES);
@ -52,28 +49,26 @@ public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl {
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) {
if (game.getCombat().getAttackingPlayerId().equals(getControllerId())) { return isControlledBy(game.getCombat().getAttackingPlayerId())
int attackerCount = 0; && game
for (UUID attackerId : game.getCombat().getAttackers()) { .getCombat()
Permanent attacker = game.getPermanent(attackerId); .getAttackers()
if (filter.match(attacker, game)) { .stream()
attackerCount++; .map(game::getPermanent)
} .filter(permanent -> filter.match(permanent, sourceId, controllerId, game))
} .mapToInt(x -> 1).sum() > minAttackers;
return attackerCount >= minAttackers;
}
return false;
} }
@Override @Override
public String getTriggerPhrase() { public String getTriggerPhrase() {
if (minAttackers == 0) { if (minAttackers == 1) {
return "Whenever you attack, "; return "Whenever you attack, ";
} }
StringBuilder sb = new StringBuilder("Whenever you attack with " + CardUtil.numberToText(minAttackers) + " or more "); StringBuilder sb = new StringBuilder("Whenever you attack with ");
sb.append(CardUtil.numberToText(minAttackers));
sb.append(" or more ");
sb.append(filter.getMessage()); sb.append(filter.getMessage());
sb.append(", "); sb.append(", ");
return sb.toString(); return sb.toString();
} }
} }