mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
Add modeTag + hint of used modes for "choose one that hasn't been chosen" abilities. (#10860)
* Add modeTag + hint of used modes on Three Bowls of Porridge * add ModesAlreadyUsedHint to all the cards.
This commit is contained in:
parent
dcca63b963
commit
be4b568e88
14 changed files with 168 additions and 15 deletions
|
|
@ -8,6 +8,7 @@ import mage.abilities.effects.common.CreateTokenAllEffect;
|
|||
import mage.abilities.effects.common.EntersBattlefieldUnderControlOfOpponentOfChoiceEffect;
|
||||
import mage.abilities.effects.common.SetPlayerLifeSourceEffect;
|
||||
import mage.abilities.effects.common.discard.DiscardHandControllerEffect;
|
||||
import mage.abilities.hint.common.ModesAlreadyUsedHint;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -32,13 +33,22 @@ public final class CaptiveAudience extends CardImpl {
|
|||
Ability ability = new BeginningOfUpkeepTriggeredAbility(
|
||||
new SetPlayerLifeSourceEffect(4), TargetController.YOU, false
|
||||
);
|
||||
ability.setModeTag("life total becomes 4");
|
||||
ability.getModes().setEachModeOnlyOnce(true);
|
||||
|
||||
// • Discard your hand.
|
||||
ability.addMode(new Mode(new DiscardHandControllerEffect()));
|
||||
ability.addMode(
|
||||
new Mode(new DiscardHandControllerEffect())
|
||||
.setModeTag("discard hand")
|
||||
);
|
||||
|
||||
// • Each opponent creates five 2/2 black Zombie creature tokens.
|
||||
ability.addMode(new Mode(new CreateTokenAllEffect(new ZombieToken(), 5, TargetController.OPPONENT)));
|
||||
ability.addMode(
|
||||
new Mode(new CreateTokenAllEffect(new ZombieToken(), 5, TargetController.OPPONENT))
|
||||
.setModeTag("opponents create Zombies")
|
||||
);
|
||||
|
||||
ability.addHint(ModesAlreadyUsedHint.instance);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
|
|
@ -11,6 +10,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
|||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.LoseGameSourceControllerEffect;
|
||||
import mage.abilities.effects.common.discard.DiscardTargetEffect;
|
||||
import mage.abilities.hint.common.ModesAlreadyUsedHint;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -18,6 +18,8 @@ import mage.constants.TargetController;
|
|||
import mage.target.common.TargetAnyTarget;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -30,6 +32,7 @@ public final class DemonicPact extends CardImpl {
|
|||
// At the beginning of your upkeep, choose one that hasn't been chosen
|
||||
// - Demonic Pact deals 4 damage to any target and you gain 4 life;
|
||||
Ability ability = new BeginningOfUpkeepTriggeredAbility(new DamageTargetEffect(4), TargetController.YOU, false);
|
||||
ability.setModeTag("deals damage and gain life");
|
||||
ability.getModes().setEachModeOnlyOnce(true);
|
||||
ability.addTarget(new TargetAnyTarget());
|
||||
Effect effect = new GainLifeEffect(4);
|
||||
|
|
@ -38,17 +41,21 @@ public final class DemonicPact extends CardImpl {
|
|||
|
||||
// - Target opponent discards two cards
|
||||
Mode mode = new Mode(new DiscardTargetEffect(2));
|
||||
mode.setModeTag("opponent discard");
|
||||
mode.addTarget(new TargetOpponent());
|
||||
ability.addMode(mode);
|
||||
|
||||
// - Draw two cards
|
||||
mode = new Mode(new DrawCardSourceControllerEffect(2));
|
||||
mode.setModeTag("draw");
|
||||
ability.addMode(mode);
|
||||
|
||||
// - You lose the game.
|
||||
mode = new Mode(new LoseGameSourceControllerEffect());
|
||||
mode.setModeTag("lose the game");
|
||||
ability.addMode(mode);
|
||||
|
||||
ability.addHint(ModesAlreadyUsedHint.instance);
|
||||
this.addAbility(ability);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import mage.abilities.common.AllianceAbility;
|
|||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.abilities.hint.common.ModesAlreadyUsedHint;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -32,14 +33,20 @@ public final class GalaGreeters extends CardImpl {
|
|||
// Alliance — Whenever another creature enters the battlefield under your control, choose one that hasn't been chosen this turn—
|
||||
// • Put a +1/+1 counter on Gala Greeters.
|
||||
Ability ability = new AllianceAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()));
|
||||
ability.setModeTag("put +1/+1 counter");
|
||||
ability.getModes().setEachModeOnlyOnce(true);
|
||||
ability.getModes().setResetEachTurn(true);
|
||||
|
||||
// • Create a tapped Treasure token.
|
||||
ability.addMode(new Mode(new CreateTokenEffect(new TreasureToken(), 1, true, false)));
|
||||
ability.addMode(
|
||||
new Mode(new CreateTokenEffect(new TreasureToken(), 1, true, false))
|
||||
.setModeTag("create tapped Treasure")
|
||||
);
|
||||
|
||||
// • You gain 2 life.
|
||||
ability.addMode(new Mode(new GainLifeEffect(2)));
|
||||
ability.addMode(new Mode(new GainLifeEffect(2)).setModeTag("gain life"));
|
||||
|
||||
ability.addHint(ModesAlreadyUsedHint.instance);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import mage.abilities.effects.common.CopyTargetSpellEffect;
|
|||
import mage.abilities.effects.common.DamagePlayersEffect;
|
||||
import mage.abilities.effects.common.MayTapOrUntapTargetEffect;
|
||||
import mage.abilities.effects.common.PutOnLibrarySourceEffect;
|
||||
import mage.abilities.hint.common.ModesAlreadyUsedHint;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -48,17 +49,29 @@ public final class GandalfTheGrey extends CardImpl {
|
|||
Ability ability = new SpellCastControllerTriggeredAbility(
|
||||
new MayTapOrUntapTargetEffect(), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false
|
||||
);
|
||||
ability.setModeTag("tap or untap");
|
||||
ability.addTarget(new TargetPermanent());
|
||||
ability.getModes().setEachModeOnlyOnce(true);
|
||||
|
||||
// * Gandalf the Grey deals 3 damage to each opponent.
|
||||
ability.addMode(new Mode(new DamagePlayersEffect(3, TargetController.OPPONENT)));
|
||||
ability.addMode(
|
||||
new Mode(new DamagePlayersEffect(3, TargetController.OPPONENT))
|
||||
.setModeTag("damage opponents")
|
||||
);
|
||||
|
||||
// * Copy target instant or sorcery spell you control. You may choose new targets for the copy.
|
||||
ability.addMode(new Mode(new CopyTargetSpellEffect()).addTarget(new TargetSpell(filter)));
|
||||
ability.addMode(
|
||||
new Mode(new CopyTargetSpellEffect()).addTarget(new TargetSpell(filter))
|
||||
.setModeTag("copy spell")
|
||||
);
|
||||
|
||||
// * Put Gandalf on top of its owner's library.
|
||||
ability.addMode(new Mode(new PutOnLibrarySourceEffect(true)));
|
||||
ability.addMode(
|
||||
new Mode(new PutOnLibrarySourceEffect(true))
|
||||
.setModeTag("put on top of library")
|
||||
);
|
||||
|
||||
ability.addHint(ModesAlreadyUsedHint.instance);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
|||
import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
|
||||
import mage.abilities.effects.common.SacrificeAllEffect;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.hint.common.ModesAlreadyUsedHint;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
|
|
@ -42,16 +43,20 @@ public final class HenrikaDomnathi extends CardImpl {
|
|||
Ability ability = new BeginningOfCombatTriggeredAbility(new SacrificeAllEffect(
|
||||
1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT
|
||||
), TargetController.YOU, false);
|
||||
ability.setModeTag("each player sacrifice");
|
||||
ability.getModes().setEachModeOnlyOnce(true);
|
||||
|
||||
// • You draw a card and you lose 1 life.
|
||||
Mode mode = new Mode(new DrawCardSourceControllerEffect(1).setText("you draw a card"));
|
||||
mode.addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and"));
|
||||
mode.setModeTag("draw and lose life");
|
||||
ability.addMode(mode);
|
||||
|
||||
// • Transform Henrika Domnathi.
|
||||
ability.addMode(new Mode(new TransformSourceEffect()));
|
||||
ability.addMode(new Mode(new TransformSourceEffect()).setModeTag("transform"));
|
||||
this.addAbility(new TransformAbility());
|
||||
|
||||
ability.addHint(ModesAlreadyUsedHint.instance);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import mage.abilities.effects.RestrictionEffect;
|
|||
import mage.abilities.effects.common.continuous.BecomesCreatureTypeTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostSourceEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
||||
import mage.abilities.hint.common.ModesAlreadyUsedHint;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
|
@ -47,6 +48,7 @@ public final class KarganIntimidator extends CardImpl {
|
|||
Ability ability = new SimpleActivatedAbility(
|
||||
new BoostSourceEffect(1, 1, Duration.EndOfTurn), new GenericManaCost(1)
|
||||
);
|
||||
ability.setModeTag("gets +1/+1");
|
||||
ability.getModes().setEachModeOnlyOnce(true);
|
||||
ability.getModes().setResetEachTurn(true);
|
||||
|
||||
|
|
@ -55,12 +57,16 @@ public final class KarganIntimidator extends CardImpl {
|
|||
Duration.EndOfTurn, SubType.COWARD
|
||||
).setText("Target creature becomes a Coward until end of turn"));
|
||||
mode.addTarget(new TargetCreaturePermanent());
|
||||
mode.setModeTag("target becomes a Coward");
|
||||
ability.addMode(mode);
|
||||
|
||||
// • Target Warrior gains trample until end of turn.
|
||||
mode = new Mode(new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn));
|
||||
mode.addTarget(new TargetPermanent(filter));
|
||||
mode.setModeTag("target gain trample");
|
||||
ability.addMode(mode);
|
||||
|
||||
ability.addHint(ModesAlreadyUsedHint.instance);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import mage.abilities.effects.common.DamageTargetEffect;
|
|||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
|
||||
import mage.abilities.hint.common.ModesAlreadyUsedHint;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
|
@ -51,18 +52,23 @@ public final class SolKanarTheTainted extends CardImpl {
|
|||
Ability ability = new BeginningOfEndStepTriggeredAbility(
|
||||
new DrawCardSourceControllerEffect(1), TargetController.YOU, false
|
||||
);
|
||||
ability.setModeTag("draw");
|
||||
ability.getModes().setEachModeOnlyOnce(true);
|
||||
|
||||
// * Each opponent loses 2 life and you gain 2 life.
|
||||
ability.addMode(new Mode(new LoseLifeOpponentsEffect(2))
|
||||
.addEffect(new GainLifeEffect(2).concatBy("and")));
|
||||
.addEffect(new GainLifeEffect(2).concatBy("and"))
|
||||
.setModeTag("opponents lose life and you gain life"));
|
||||
|
||||
// * Sol'Kanar the Tainted deals 3 damage to up to one other target creature or planeswalker.
|
||||
ability.addMode(new Mode(new DamageTargetEffect(3))
|
||||
.addTarget(new TargetPermanent(0, 1, filter)));
|
||||
.addTarget(new TargetPermanent(0, 1, filter))
|
||||
.setModeTag("deals damage"));
|
||||
|
||||
// * Exile Sol'Kanar, then return it to the battlefield under an opponent's control.
|
||||
ability.addMode(new Mode(new SolKanarTheTaintedEffect()));
|
||||
ability.addMode(new Mode(new SolKanarTheTaintedEffect()).setModeTag("exile then return"));
|
||||
|
||||
ability.addHint(ModesAlreadyUsedHint.instance);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import mage.abilities.effects.common.DamageTargetEffect;
|
|||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.SacrificeSourceEffect;
|
||||
import mage.abilities.effects.common.TapTargetEffect;
|
||||
import mage.abilities.hint.common.ModesAlreadyUsedHint;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -18,14 +19,12 @@ import mage.target.common.TargetCreaturePermanent;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class ThreeBowlsOfPorridge extends CardImpl {
|
||||
|
||||
public ThreeBowlsOfPorridge(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
|
||||
|
||||
this.subtype.add(SubType.FOOD);
|
||||
|
||||
// {2}, {T}: Choose one that hasn't been chosen --
|
||||
|
|
@ -36,17 +35,21 @@ public final class ThreeBowlsOfPorridge extends CardImpl {
|
|||
);
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
ability.setModeTag("deals damage");
|
||||
ability.getModes().setEachModeOnlyOnce(true);
|
||||
|
||||
// * Tap target creature.
|
||||
Mode mode = new Mode(new TapTargetEffect());
|
||||
mode.addTarget(new TargetCreaturePermanent());
|
||||
mode.setModeTag("tap");
|
||||
ability.addMode(mode);
|
||||
|
||||
// * Sacrifice Three Bowls of Porridge. You gain 3 life.
|
||||
mode = new Mode(new SacrificeSourceEffect());
|
||||
mode.addEffect(new GainLifeEffect(3));
|
||||
mode.setModeTag("sacrifice and gain life");
|
||||
ability.addMode(mode);
|
||||
ability.addHint(ModesAlreadyUsedHint.instance);
|
||||
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
@ -59,4 +62,4 @@ public final class ThreeBowlsOfPorridge extends CardImpl {
|
|||
public ThreeBowlsOfPorridge copy() {
|
||||
return new ThreeBowlsOfPorridge(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -557,6 +557,11 @@ public interface Ability extends Controllable, Serializable {
|
|||
|
||||
Ability addHint(Hint hint);
|
||||
|
||||
/**
|
||||
* Tag the current mode to be retrieved elsewhere thanks to the tag.
|
||||
*/
|
||||
void setModeTag(String tag);
|
||||
|
||||
/**
|
||||
* For abilities with static icons
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1390,6 +1390,16 @@ public abstract class AbilityImpl implements Ability {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the mode tag for the current mode.
|
||||
*/
|
||||
@Override
|
||||
public void setModeTag(String tag) {
|
||||
if (getModes().getMode() != null) {
|
||||
getModes().getMode().setModeTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<CardIcon> getIcons() {
|
||||
return getIcons(null);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ public class Mode implements Serializable {
|
|||
protected final Targets targets;
|
||||
protected final Effects effects;
|
||||
protected String flavorWord;
|
||||
/**
|
||||
* Optional Tag to distinguish this mode from others.
|
||||
* In the case of modes that players can only choose once,
|
||||
* the tag is directly what is displayed in ModesAlreadyUsedHint
|
||||
*/
|
||||
protected String modeTag;
|
||||
|
||||
public Mode(Effect effect) {
|
||||
this.id = UUID.randomUUID();
|
||||
|
|
@ -32,6 +38,7 @@ public class Mode implements Serializable {
|
|||
this.targets = mode.targets.copy();
|
||||
this.effects = mode.effects.copy();
|
||||
this.flavorWord = mode.flavorWord;
|
||||
this.modeTag = mode.modeTag;
|
||||
}
|
||||
|
||||
public UUID setRandomId() {
|
||||
|
|
@ -71,6 +78,21 @@ public class Mode implements Serializable {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag the mode to be retrieved elsewhere thanks to the tag.
|
||||
*/
|
||||
public Mode setModeTag(String tag) {
|
||||
this.modeTag = tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the mode tag for this mode, if set
|
||||
*/
|
||||
public String getModeTag() {
|
||||
return this.modeTag == null ? "" : this.modeTag;
|
||||
}
|
||||
|
||||
public String getFlavorWord() {
|
||||
return flavorWord;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import mage.util.CardUtil;
|
|||
import mage.util.RandomUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -96,6 +97,15 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
return modeToGet;
|
||||
}
|
||||
|
||||
public Stream<Mode> stream() {
|
||||
return super.values().stream();
|
||||
}
|
||||
|
||||
public Stream<Mode> streamAlreadySelected(Ability source, Game game) {
|
||||
Set<UUID> selected = getAlreadySelectedModes(source, game);
|
||||
return stream().filter(m -> selected.contains(m.getId()));
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return currentMode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package mage.abilities.hint.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Hint at all the used modes for modal effects that use
|
||||
* "Choose one that hasn't been chosen" or "Choose one that hasn't been chosen this turn".
|
||||
* <p>
|
||||
* Note: the modes need to be set up with a modeTag in order for the hint to find them.
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public enum ModesAlreadyUsedHint implements Hint {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public String getText(Game game, Ability ability) {
|
||||
List<String> used = ability
|
||||
.getModes()
|
||||
.streamAlreadySelected(ability, game)
|
||||
.map(Mode::getModeTag)
|
||||
.filter(tag -> tag != null && !tag.isEmpty())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (used.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return "Already used"
|
||||
+ (ability.getModes().isResetEachTurn() ? " this turn" : "")
|
||||
+ ": [" + String.join(", ", used) + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModesAlreadyUsedHint copy() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -699,6 +699,11 @@ public class StackAbility extends StackObjectImpl implements Ability {
|
|||
throw new IllegalArgumentException("Stack ability is not supports hint adding");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModeTag(String tag) {
|
||||
throw new IllegalArgumentException("Stack ability does not supports setting modeTag");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CardIcon> getIcons() {
|
||||
return this.ability.getIcons();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue