Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SLD] Implement Captain America, First Avenger #13023

Merged
merged 6 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 215 additions & 0 deletions Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java
Original file line number Diff line number Diff line change
@@ -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.
xenohedron marked this conversation as resolved.
Show resolved Hide resolved
@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);
Grath marked this conversation as resolved.
Show resolved Hide resolved
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);
}
}
1 change: 1 addition & 0 deletions Mage.Sets/src/mage/sets/SecretLairDrop.java
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,7 @@ private SecretLairDrop() {
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("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("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));
Expand Down
16 changes: 16 additions & 0 deletions Mage/src/main/java/mage/abilities/AbilityImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import mage.abilities.condition.Condition;
import mage.abilities.costs.*;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
Expand Down Expand Up @@ -330,6 +331,10 @@ public boolean activate(Game game, Set<MageIdentifier> allowedIdentifiers, boole

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
* If the spell is modal the player announces the mode choice (see rule 700.2).
*/
Expand Down Expand Up @@ -649,6 +654,17 @@ private void handlePhyrexianManaCosts(Game game, Player controller) {
}
}

/**
* 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.
*
Expand Down
25 changes: 25 additions & 0 deletions Mage/src/main/java/mage/abilities/costs/EarlyTargetCost.java
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ public DamageMultiEffect(int amount) {
}

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;
}

Expand Down
Loading