forked from External/mage
[SLD] Implement Captain America, First Avenger (#13023)
* [SLD] Implement Captain America, First Avenger I made assumptions that WotC is going to fix the rules by adding "choose the equipment you're unattaching with Throw..." to rule 601.2b so that this card actually functions since you have to choose a TargetAnyTargetAmount in steps 601.2c/601.2d long before you actually pay the unattach cost in 601.2h. * Remove Target workaround, add proper 601.2b handling for choosing cost targets early using inheritance to avoid having a horrific brittle list of 'these costs must be paid early'.
This commit is contained in:
parent
6d84cee967
commit
f7f2d58081
5 changed files with 262 additions and 1 deletions
215
Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java
Normal file
215
Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
package mage.cards.c;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.MageObject;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.BeginningOfCombatTriggeredAbility;
|
||||||
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
|
import mage.abilities.costs.Cost;
|
||||||
|
import mage.abilities.costs.EarlyTargetCost;
|
||||||
|
import mage.abilities.costs.mana.GenericManaCost;
|
||||||
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.DamageMultiEffect;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterControlledPermanent;
|
||||||
|
import mage.filter.common.FilterEquipmentPermanent;
|
||||||
|
import mage.filter.predicate.ObjectSourcePlayer;
|
||||||
|
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||||
|
import mage.filter.predicate.permanent.AttachedToPredicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.Target;
|
||||||
|
import mage.target.TargetPermanent;
|
||||||
|
import mage.target.common.TargetAnyTargetAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Grath
|
||||||
|
*/
|
||||||
|
public final class CaptainAmericaFirstAvenger extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterEquipmentPermanent("Equipment you control");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(TargetController.YOU.getControllerPredicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public CaptainAmericaFirstAvenger(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}{U}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.SOLDIER);
|
||||||
|
this.subtype.add(SubType.HERO);
|
||||||
|
this.power = new MageInt(4);
|
||||||
|
this.toughness = new MageInt(4);
|
||||||
|
|
||||||
|
// Throw ... — {3}, Unattach an Equipment from Captain America: He deals damage equal to that Equipment’s mana value divided as you choose among one, two, or three targets.
|
||||||
|
Ability ability = new SimpleActivatedAbility(
|
||||||
|
new DamageMultiEffect(CaptainAmericaFirstAvengerValue.instance).setText(
|
||||||
|
"he deals damage equal to that Equipment's mana value divided as you choose among one, two, or three targets."),
|
||||||
|
new GenericManaCost(3));
|
||||||
|
ability.addCost(new CaptainAmericaFirstAvengerUnattachCost());
|
||||||
|
ability.addTarget(new TargetAnyTargetAmount(CaptainAmericaFirstAvengerValue.instance, 3));
|
||||||
|
this.addAbility(ability.withFlavorWord("Throw ..."));
|
||||||
|
|
||||||
|
// ... Catch — At the beginning of combat on your turn, attach up to one target Equipment you control to Captain America.
|
||||||
|
ability = new BeginningOfCombatTriggeredAbility(
|
||||||
|
new CaptainAmericaFirstAvengerCatchEffect(), TargetController.YOU, false
|
||||||
|
);
|
||||||
|
ability.addTarget(new TargetPermanent(0, 1, filter));
|
||||||
|
this.addAbility(ability.withFlavorWord("... Catch"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CaptainAmericaFirstAvenger(final CaptainAmericaFirstAvenger card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CaptainAmericaFirstAvenger copy() {
|
||||||
|
return new CaptainAmericaFirstAvenger(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CaptainAmericaPredicate implements ObjectSourcePlayerPredicate<MageObject> {
|
||||||
|
instance;
|
||||||
|
|
||||||
|
// Functional negation of AnotherPredicate.
|
||||||
|
@Override
|
||||||
|
public boolean apply(ObjectSourcePlayer<MageObject> input, Game game) {
|
||||||
|
if (!input.getObject().getId().equals(input.getSourceId())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int zcc = input.getSource().getSourceObjectZoneChangeCounter();
|
||||||
|
return zcc == input.getObject().getZoneChangeCounter(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{this}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CaptainAmericaFirstAvengerValue implements DynamicValue {
|
||||||
|
instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||||
|
int amount = 0;
|
||||||
|
for (Cost cost : sourceAbility.getCosts()) {
|
||||||
|
if (cost instanceof CaptainAmericaFirstAvengerUnattachCost && !cost.getTargets().isEmpty()) {
|
||||||
|
Permanent equipment = game.getPermanentOrLKIBattlefield(cost.getTargets().getFirstTarget());
|
||||||
|
if (equipment != null) {
|
||||||
|
amount = equipment.getManaValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicValue copy() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "X";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "that Equipment's mana value";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CaptainAmericaFirstAvengerUnattachCost extends EarlyTargetCost {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterEquipmentPermanent("equipment attached to this creature");
|
||||||
|
private static final FilterPermanent subfilter = new FilterControlledPermanent("{this}");
|
||||||
|
|
||||||
|
static {
|
||||||
|
subfilter.add(CaptainAmericaPredicate.instance);
|
||||||
|
filter.add(new AttachedToPredicate(subfilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptainAmericaFirstAvengerUnattachCost() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CaptainAmericaFirstAvengerUnattachCost(final CaptainAmericaFirstAvengerUnattachCost cost) {
|
||||||
|
super(cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
|
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||||
|
return permanent != null
|
||||||
|
&& !permanent.getAttachments().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||||
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
|
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||||
|
if (permanent == null || player == null) {
|
||||||
|
return paid;
|
||||||
|
}
|
||||||
|
Permanent equipment = game.getPermanentOrLKIBattlefield(getTargets().getFirstTarget());
|
||||||
|
if (equipment == null || !permanent.getAttachments().contains(equipment.getId()) ||
|
||||||
|
!player.chooseUse(Outcome.Benefit, "Unattach " + equipment.getIdName() + "?", source, game)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
paid = permanent.removeAttachment(equipment.getId(), source, game);
|
||||||
|
|
||||||
|
return paid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CaptainAmericaFirstAvengerUnattachCost copy() {
|
||||||
|
return new CaptainAmericaFirstAvengerUnattachCost(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void chooseTarget(Game game, Ability source, Player controller) {
|
||||||
|
Target chosenEquipment = new TargetPermanent(1, 1, filter, true);
|
||||||
|
controller.choose(Outcome.Benefit, chosenEquipment, source, game);
|
||||||
|
addTarget(chosenEquipment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return "Unattach an Equipment from {this}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CaptainAmericaFirstAvengerCatchEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
CaptainAmericaFirstAvengerCatchEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "attach target Equipment you control to {this}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private CaptainAmericaFirstAvengerCatchEffect(final CaptainAmericaFirstAvengerCatchEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CaptainAmericaFirstAvengerCatchEffect copy() {
|
||||||
|
return new CaptainAmericaFirstAvengerCatchEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Permanent equipment = game.getPermanent(this.getTargetPointer().getFirst(game, source));
|
||||||
|
Permanent creature = source.getSourcePermanentIfItStillExists(game);
|
||||||
|
return equipment != null && creature != null && creature.addAttachment(equipment.getId(), source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1380,6 +1380,7 @@ public class SecretLairDrop extends ExpansionSet {
|
||||||
cards.add(new SetCardInfo("Mayhem Devil", 1715, Rarity.RARE, mage.cards.m.MayhemDevil.class));
|
cards.add(new SetCardInfo("Mayhem Devil", 1715, Rarity.RARE, mage.cards.m.MayhemDevil.class));
|
||||||
cards.add(new SetCardInfo("Moldervine Reclamation", 1716, Rarity.RARE, mage.cards.m.MoldervineReclamation.class));
|
cards.add(new SetCardInfo("Moldervine Reclamation", 1716, Rarity.RARE, mage.cards.m.MoldervineReclamation.class));
|
||||||
cards.add(new SetCardInfo("Prossh, Skyraider of Kher", 1717, Rarity.MYTHIC, mage.cards.p.ProsshSkyraiderOfKher.class));
|
cards.add(new SetCardInfo("Prossh, Skyraider of Kher", 1717, Rarity.MYTHIC, mage.cards.p.ProsshSkyraiderOfKher.class));
|
||||||
|
cards.add(new SetCardInfo("Captain America, First Avenger", 1726, Rarity.MYTHIC, mage.cards.c.CaptainAmericaFirstAvenger.class));
|
||||||
cards.add(new SetCardInfo("Iron Man, Titan of Innovation", 1731, Rarity.MYTHIC, mage.cards.i.IronManTitanOfInnovation.class));
|
cards.add(new SetCardInfo("Iron Man, Titan of Innovation", 1731, Rarity.MYTHIC, mage.cards.i.IronManTitanOfInnovation.class));
|
||||||
cards.add(new SetCardInfo("Wolverine, Best There Is", 1737, Rarity.MYTHIC, mage.cards.w.WolverineBestThereIs.class));
|
cards.add(new SetCardInfo("Wolverine, Best There Is", 1737, Rarity.MYTHIC, mage.cards.w.WolverineBestThereIs.class));
|
||||||
cards.add(new SetCardInfo("Jace, the Mind Sculptor", 8001, Rarity.MYTHIC, mage.cards.j.JaceTheMindSculptor.class));
|
cards.add(new SetCardInfo("Jace, the Mind Sculptor", 8001, Rarity.MYTHIC, mage.cards.j.JaceTheMindSculptor.class));
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import mage.abilities.common.EntersBattlefieldAbility;
|
||||||
import mage.abilities.condition.Condition;
|
import mage.abilities.condition.Condition;
|
||||||
import mage.abilities.costs.*;
|
import mage.abilities.costs.*;
|
||||||
import mage.abilities.costs.common.PayLifeCost;
|
import mage.abilities.costs.common.PayLifeCost;
|
||||||
|
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||||
import mage.abilities.costs.mana.ManaCost;
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
import mage.abilities.costs.mana.ManaCosts;
|
import mage.abilities.costs.mana.ManaCosts;
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
|
@ -330,6 +331,10 @@ public abstract class AbilityImpl implements Ability {
|
||||||
|
|
||||||
handlePhyrexianManaCosts(game, controller);
|
handlePhyrexianManaCosts(game, controller);
|
||||||
|
|
||||||
|
// 20241022 - 601.2b
|
||||||
|
// Not yet included in 601.2b but this is where it will be
|
||||||
|
handleChooseCostTargets(game, controller);
|
||||||
|
|
||||||
/* 20130201 - 601.2b
|
/* 20130201 - 601.2b
|
||||||
* If the spell is modal the player announces the mode choice (see rule 700.2).
|
* If the spell is modal the player announces the mode choice (see rule 700.2).
|
||||||
*/
|
*/
|
||||||
|
|
@ -649,6 +654,17 @@ public abstract class AbilityImpl implements Ability {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 601.2b Choose targets for costs that have to be chosen early.
|
||||||
|
*/
|
||||||
|
private void handleChooseCostTargets(Game game, Player controller) {
|
||||||
|
for (Cost cost : getCosts()) {
|
||||||
|
if (cost instanceof EarlyTargetCost && cost.getTargets().isEmpty()) {
|
||||||
|
((EarlyTargetCost) cost).chooseTarget(game, this, controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles X mana costs and sets manaCostsToPay.
|
* Handles X mana costs and sets manaCostsToPay.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
25
Mage/src/main/java/mage/abilities/costs/EarlyTargetCost.java
Normal file
25
Mage/src/main/java/mage/abilities/costs/EarlyTargetCost.java
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package mage.abilities.costs;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Grath
|
||||||
|
* Costs which extend this class need to have targets chosen, and those targets must be chosen during 601.2b step.
|
||||||
|
*/
|
||||||
|
public abstract class EarlyTargetCost extends CostImpl {
|
||||||
|
|
||||||
|
protected EarlyTargetCost() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EarlyTargetCost(final EarlyTargetCost cost) {
|
||||||
|
super(cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract EarlyTargetCost copy();
|
||||||
|
|
||||||
|
public abstract void chooseTarget(Game game, Ability source, Player controller);
|
||||||
|
}
|
||||||
|
|
@ -28,7 +28,11 @@ public class DamageMultiEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public DamageMultiEffect(int amount, String whoDealDamageName) {
|
public DamageMultiEffect(int amount, String whoDealDamageName) {
|
||||||
this(StaticValue.get(amount));
|
this(StaticValue.get(amount), whoDealDamageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DamageMultiEffect(DynamicValue amount, String whoDealDamageName) {
|
||||||
|
this(amount);
|
||||||
this.sourceName = whoDealDamageName;
|
this.sourceName = whoDealDamageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue