Merge pull request #6507 from emerald000/abilityResolved

Refactor and add hint for "Ability resolved X times"
This commit is contained in:
Oleg Agafonov 2020-05-04 08:54:40 +02:00 committed by GitHub
commit 8a3ba6729f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 158 additions and 76 deletions

View file

@ -6,7 +6,9 @@ import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageEverythingEffect; import mage.abilities.effects.common.DamageEverythingEffect;
import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.hint.common.AbilityResolutionCountHint;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -17,7 +19,6 @@ import mage.counters.CounterType;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.watchers.common.AbilityResolvedWatcher; import mage.watchers.common.AbilityResolvedWatcher;
import java.util.UUID; import java.util.UUID;
@ -40,7 +41,8 @@ public final class AshlingThePilgrim extends CardImpl {
Ability ability = new SimpleActivatedAbility( Ability ability = new SimpleActivatedAbility(
new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{1}{R}") new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl("{1}{R}")
); );
ability.addEffect(new AshlingThePilgrimEffect()); ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.Damage, 3, new AshlingThePilgrimEffect()));
ability.addHint(AbilityResolutionCountHint.instance);
this.addAbility(ability, new AbilityResolvedWatcher()); this.addAbility(ability, new AbilityResolvedWatcher());
} }
@ -58,8 +60,7 @@ class AshlingThePilgrimEffect extends OneShotEffect {
AshlingThePilgrimEffect() { AshlingThePilgrimEffect() {
super(Outcome.Damage); super(Outcome.Damage);
this.staticText = "If this is the third time this ability has resolved this turn, " + this.staticText = "remove all +1/+1 counters from {this}, and it deals that much damage to each creature and each player";
"remove all +1/+1 counters from {this}, and it deals that much damage to each creature and each player";
} }
private AshlingThePilgrimEffect(final AshlingThePilgrimEffect effect) { private AshlingThePilgrimEffect(final AshlingThePilgrimEffect effect) {
@ -73,15 +74,8 @@ class AshlingThePilgrimEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId());
AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class); if (sourcePermanent != null) {
if (controller == null
|| sourcePermanent == null
|| watcher == null
|| !watcher.checkActivations(source, game)) {
return false;
}
int counters = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); int counters = sourcePermanent.getCounters(game).getCount(CounterType.P1P1);
if (counters < 1) { if (counters < 1) {
return false; return false;
@ -89,4 +83,6 @@ class AshlingThePilgrimEffect extends OneShotEffect {
sourcePermanent.removeCounters(CounterType.P1P1.createInstance(counters), game); sourcePermanent.removeCounters(CounterType.P1P1.createInstance(counters), game);
return new DamageEverythingEffect(counters, StaticFilters.FILTER_PERMANENT_CREATURE).apply(game, source); return new DamageEverythingEffect(counters, StaticFilters.FILTER_PERMANENT_CREATURE).apply(game, source);
} }
return true;
}
} }

View file

@ -4,9 +4,11 @@ import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect;
import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.hint.common.AbilityResolutionCountHint;
import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -15,8 +17,6 @@ import mage.constants.Duration;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.Player;
import mage.watchers.common.AbilityResolvedWatcher; import mage.watchers.common.AbilityResolvedWatcher;
import java.util.UUID; import java.util.UUID;
@ -37,7 +37,9 @@ public final class InnerFlameIgniter extends CardImpl {
Ability ability = new SimpleActivatedAbility( Ability ability = new SimpleActivatedAbility(
new BoostControlledEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{2}{R}") new BoostControlledEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{2}{R}")
); );
ability.addEffect(new InnerFlameIgniterEffect()); ContinuousEffect effectIf3rdResolution = new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES);
ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.AddAbility, 3, effectIf3rdResolution));
ability.addHint(AbilityResolutionCountHint.instance);
this.addAbility(ability, new AbilityResolvedWatcher()); this.addAbility(ability, new AbilityResolvedWatcher());
} }
@ -50,37 +52,3 @@ public final class InnerFlameIgniter extends CardImpl {
return new InnerFlameIgniter(this); return new InnerFlameIgniter(this);
} }
} }
class InnerFlameIgniterEffect extends OneShotEffect {
InnerFlameIgniterEffect() {
super(Outcome.AddAbility);
this.staticText = "If this is the third time this ability has resolved this turn, " +
"creatures you control gain first strike until end of turn";
}
private InnerFlameIgniterEffect(final InnerFlameIgniterEffect effect) {
super(effect);
}
@Override
public InnerFlameIgniterEffect copy() {
return new InnerFlameIgniterEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class);
if (controller == null
|| watcher == null
|| !watcher.checkActivations(source, game)) {
return false;
}
game.addEffect(new GainAbilityControlledEffect(
FirstStrikeAbility.getInstance(), Duration.EndOfTurn,
StaticFilters.FILTER_PERMANENT_CREATURE
), source);
return true;
}
}

View file

@ -6,7 +6,9 @@ import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.hint.common.AbilityResolutionCountHint;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -37,8 +39,9 @@ public final class SoulbrightFlamekin extends CardImpl {
Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect(
TrampleAbility.getInstance(), Duration.EndOfTurn TrampleAbility.getInstance(), Duration.EndOfTurn
), new ManaCostsImpl("{2}")); ), new ManaCostsImpl("{2}"));
ability.addEffect(new SoulbrightFlamekinEffect()); ability.addEffect(new IfAbilityHasResolvedXTimesEffect(Outcome.PutManaInPool, 3, new SoulbrightFlamekinEffect()));
ability.addTarget(new TargetCreaturePermanent()); ability.addTarget(new TargetCreaturePermanent());
ability.addHint(AbilityResolutionCountHint.instance);
this.addAbility(ability, new AbilityResolvedWatcher()); this.addAbility(ability, new AbilityResolvedWatcher());
} }
@ -56,8 +59,7 @@ class SoulbrightFlamekinEffect extends OneShotEffect {
SoulbrightFlamekinEffect() { SoulbrightFlamekinEffect() {
super(Outcome.Damage); super(Outcome.Damage);
this.staticText = "If this is the third time this ability has resolved this turn, " + this.staticText = "you may add {R}{R}{R}{R}{R}{R}{R}{R}";
"you may add {R}{R}{R}{R}{R}{R}{R}{R}";
} }
private SoulbrightFlamekinEffect(final SoulbrightFlamekinEffect effect) { private SoulbrightFlamekinEffect(final SoulbrightFlamekinEffect effect) {
@ -72,14 +74,10 @@ class SoulbrightFlamekinEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class); if (controller != null && controller.chooseUse(Outcome.PutManaInPool, "Add {R}{R}{R}{R}{R}{R}{R}{R}?", source, game)) {
if (controller == null
|| watcher == null
|| !watcher.checkActivations(source, game)
|| !controller.chooseUse(Outcome.PutManaInPool, "Add {R}{R}{R}{R}{R}{R}{R}{R}}?", source, game)) {
return false;
}
controller.getManaPool().addMana(Mana.RedMana(8), game, source); controller.getManaPool().addMana(Mana.RedMana(8), game, source);
return true; return true;
} }
return false;
}
} }

View file

@ -152,6 +152,9 @@ public abstract class AbilityImpl implements Ability {
boolean result = true; boolean result = true;
//20100716 - 117.12 //20100716 - 117.12
if (checkIfClause(game)) { if (checkIfClause(game)) {
// Ability has started resolving. Fire event.
// Used for abilities counting the number of resolutions like Ashling the Pilgrim.
game.fireEvent(new GameEvent(GameEvent.EventType.RESOLVING_ABILITY, this.getOriginalId(), this.getSourceId(), this.getControllerId()));
if (this instanceof TriggeredAbility) { if (this instanceof TriggeredAbility) {
for (UUID modeId : this.getModes().getSelectedModes()) { for (UUID modeId : this.getModes().getSelectedModes()) {
this.getModes().setActiveMode(modeId); this.getModes().setActiveMode(modeId);

View file

@ -0,0 +1,39 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.game.Game;
import mage.watchers.common.AbilityResolvedWatcher;
/**
* @author emerald000
*/
public enum AbilityResolutionCount implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class);
if (watcher != null) {
return watcher.getResolutionCount(game, sourceAbility);
}
return 0;
}
@Override
public AbilityResolutionCount copy() {
return instance;
}
@Override
public String toString() {
return "X";
}
@Override
public String getMessage() {
return "permanents you control";
}
}

View file

@ -0,0 +1,52 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.util.CardUtil;
import mage.watchers.common.AbilityResolvedWatcher;
/**
* @author emerald000
*/
public class IfAbilityHasResolvedXTimesEffect extends OneShotEffect {
private final int resolutionNumber;
private final Effect effect;
public IfAbilityHasResolvedXTimesEffect(Outcome outcome, int resolutionNumber, Effect effect) {
super(outcome);
this.resolutionNumber = resolutionNumber;
this.effect = effect;
this.staticText = "If this is the " + CardUtil.numberToOrdinalText(resolutionNumber) + " time this ability has resolved this turn, " +
effect.getText(null);
}
private IfAbilityHasResolvedXTimesEffect(final IfAbilityHasResolvedXTimesEffect effect) {
super(effect);
this.resolutionNumber = effect.resolutionNumber;
this.effect = effect.effect;
}
@Override
public IfAbilityHasResolvedXTimesEffect copy() {
return new IfAbilityHasResolvedXTimesEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
AbilityResolvedWatcher watcher = game.getState().getWatcher(AbilityResolvedWatcher.class);
if (watcher != null && watcher.getResolutionCount(game, source) == resolutionNumber) {
if (effect instanceof OneShotEffect) {
return effect.apply(game, source);
} else {
game.addEffect((ContinuousEffect) effect, source);
return true;
}
}
return true;
}
}

View file

@ -0,0 +1,26 @@
package mage.abilities.hint.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.common.AbilityResolutionCount;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.game.Game;
/**
* @author emerald000
*/
public enum AbilityResolutionCountHint implements Hint {
instance;
private static final Hint hint = new ValueHint("Resolution count:", AbilityResolutionCount.instance);
@Override
public String getText(Game game, Ability ability) {
return hint.getText(game, ability);
}
@Override
public Hint copy() {
return instance;
}
}

View file

@ -150,7 +150,8 @@ public class GameEvent implements Serializable {
SPELL_CAST, SPELL_CAST,
ACTIVATE_ABILITY, ACTIVATED_ABILITY, ACTIVATE_ABILITY, ACTIVATED_ABILITY,
TRIGGERED_ABILITY, TRIGGERED_ABILITY,
COPY_STACKOBJECT,COPIED_STACKOBJECT, RESOLVING_ABILITY,
COPY_STACKOBJECT, COPIED_STACKOBJECT,
/* ADD_MANA /* ADD_MANA
targetId id of the ability that added the mana targetId id of the ability that added the mana
sourceId sourceId of the ability that added the mana sourceId sourceId of the ability that added the mana

View file

@ -1,6 +1,5 @@
package mage.watchers.common; package mage.watchers.common;
import mage.MageObjectReference;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.constants.WatcherScope; import mage.constants.WatcherScope;
import mage.game.Game; import mage.game.Game;
@ -9,14 +8,13 @@ import mage.watchers.Watcher;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID;
/** /**
* @author TheElk801 * @author TheElk801
*/ */
public class AbilityResolvedWatcher extends Watcher { public class AbilityResolvedWatcher extends Watcher {
private final Map<MageObjectReference, Map<UUID, Integer>> activationMap = new HashMap<>(); private final Map<String, Integer> resolutionMap = new HashMap<>();
public AbilityResolvedWatcher() { public AbilityResolvedWatcher() {
super(WatcherScope.GAME); super(WatcherScope.GAME);
@ -24,17 +22,18 @@ public class AbilityResolvedWatcher extends Watcher {
@Override @Override
public void watch(GameEvent event, Game game) { public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.RESOLVING_ABILITY) {
resolutionMap.merge(event.getTargetId().toString() + game.getState().getZoneChangeCounter(event.getSourceId()), 1, Integer::sum);
}
} }
@Override @Override
public void reset() { public void reset() {
super.reset(); super.reset();
activationMap.clear(); resolutionMap.clear();
} }
public boolean checkActivations(Ability source, Game game) { public int getResolutionCount(Game game, Ability source) {
return activationMap.computeIfAbsent(new MageObjectReference( return resolutionMap.getOrDefault(source.getOriginalId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()), 0);
source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game
), x -> new HashMap<>()).compute(source.getOriginalId(), (u, i) -> i == null ? 1 : i + 1).intValue() == 3;
} }
} }