mirror of
https://github.com/magefree/mage.git
synced 2025-12-28 06:22:01 -08:00
Ready for review: Implement Craft mechanic (#11352)
* [LCI] Implement Spring-Loaded Sawblades / Bladewheel Chariot * [LCI] Implement Sunbird Standard / Sunbird Effigy * card filter needs to have an owner predicate * [LCI] Implement Throne of the Grim Captain / The Grim Captain * make default constructor for craft with artifact * dedupe some code * refactor constructors for simplicity * add currently failing test
This commit is contained in:
parent
0f7db0c69d
commit
bc4aa6931f
16 changed files with 884 additions and 8 deletions
|
|
@ -56,8 +56,12 @@ public abstract class RoleAssignment<T> implements Serializable {
|
|||
}
|
||||
|
||||
public int getRoleCount(Cards cards, Game game) {
|
||||
return getRoleCount(cards.getCards(game), game);
|
||||
}
|
||||
|
||||
public int getRoleCount(Set<? extends Card> cards, Game game) {
|
||||
Map<UUID, Set<T>> attributeMap = new HashMap<>();
|
||||
cards.getCards(game).forEach(card -> attributeMap.put(card.getId(), this.makeSet(card, game)));
|
||||
cards.forEach(card -> attributeMap.put(card.getId(), this.makeSet(card, game)));
|
||||
if (attributeMap.size() < 2) {
|
||||
return attributeMap.size();
|
||||
}
|
||||
|
|
|
|||
176
Mage/src/main/java/mage/abilities/keyword/CraftAbility.java
Normal file
176
Mage/src/main/java/mage/abilities/keyword/CraftAbility.java
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.abilities.costs.common.ExileSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterArtifactCard;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.common.FilterOwnedCard;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInGraveyardBattlefieldOrStack;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class CraftAbility extends ActivatedAbilityImpl {
|
||||
|
||||
private static final FilterCard artifactFilter = new FilterArtifactCard("artifact");
|
||||
|
||||
static {
|
||||
artifactFilter.add(TargetController.YOU.getOwnerPredicate());
|
||||
}
|
||||
|
||||
private final String description;
|
||||
private final String manaString;
|
||||
|
||||
public CraftAbility(String manaString) {
|
||||
this(manaString, "artifact", "another artifact you control or an artifact card from your graveyard", CardType.ARTIFACT.getPredicate());
|
||||
}
|
||||
|
||||
public CraftAbility(String manaString, String description, String targetDescription, Predicate<MageObject>... predicates) {
|
||||
this(manaString, description, targetDescription, 1, 1, predicates);
|
||||
}
|
||||
|
||||
public CraftAbility(String manaString, String description, String targetDescription, int minTargets, int maxTargets, Predicate<MageObject>... predicates) {
|
||||
this(manaString, description, makeTarget(minTargets, maxTargets, targetDescription, predicates));
|
||||
}
|
||||
|
||||
public CraftAbility(String manaString, String description, TargetCardInGraveyardBattlefieldOrStack target) {
|
||||
super(Zone.BATTLEFIELD, new CraftEffect(), new ManaCostsImpl<>(manaString));
|
||||
this.addCost(new ExileSourceCost());
|
||||
this.addCost(new CraftCost(target));
|
||||
this.addSubAbility(new TransformAbility());
|
||||
this.timing = TimingRule.SORCERY;
|
||||
this.manaString = manaString;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
private CraftAbility(final CraftAbility ability) {
|
||||
super(ability);
|
||||
this.manaString = ability.manaString;
|
||||
this.description = ability.description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftAbility copy() {
|
||||
return new CraftAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Craft with " + description + ' ' + manaString;
|
||||
}
|
||||
|
||||
private static TargetCardInGraveyardBattlefieldOrStack makeTarget(int minTargets, int maxTargets, String targetDescription, Predicate<MageObject>... predicates) {
|
||||
FilterPermanent filterPermanent = new FilterControlledPermanent();
|
||||
filterPermanent.add(AnotherPredicate.instance);
|
||||
FilterCard filterCard = new FilterOwnedCard();
|
||||
for (Predicate<MageObject> predicate : predicates) {
|
||||
filterPermanent.add(predicate);
|
||||
filterCard.add(predicate);
|
||||
}
|
||||
return new TargetCardInGraveyardBattlefieldOrStack(minTargets, maxTargets, filterCard, filterPermanent, targetDescription);
|
||||
}
|
||||
}
|
||||
|
||||
class CraftCost extends CostImpl {
|
||||
|
||||
private final TargetCardInGraveyardBattlefieldOrStack target;
|
||||
|
||||
CraftCost(TargetCardInGraveyardBattlefieldOrStack target) {
|
||||
super();
|
||||
this.target = target;
|
||||
target.withNotTarget(true);
|
||||
}
|
||||
|
||||
private CraftCost(final CraftCost cost) {
|
||||
super(cost);
|
||||
this.target = cost.target.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftCost copy() {
|
||||
return new CraftCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||
return target.canChoose(controllerId, source, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
paid = false;
|
||||
return paid;
|
||||
}
|
||||
player.chooseTarget(Outcome.Exile, target, source, game);
|
||||
Set<Card> cards = target
|
||||
.getTargets()
|
||||
.stream()
|
||||
.map(uuid -> {
|
||||
Permanent permanent = game.getPermanent(uuid);
|
||||
if (permanent != null) {
|
||||
return permanent;
|
||||
}
|
||||
return game.getCard(uuid);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
player.moveCardsToExile(
|
||||
cards, source, game, true,
|
||||
CardUtil.getExileZoneId(game, source),
|
||||
CardUtil.getSourceName(game, source)
|
||||
);
|
||||
paid = true;
|
||||
return paid;
|
||||
}
|
||||
}
|
||||
|
||||
class CraftEffect extends OneShotEffect {
|
||||
|
||||
CraftEffect() {
|
||||
super(Outcome.Benefit);
|
||||
}
|
||||
|
||||
private CraftEffect(final CraftEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftEffect copy() {
|
||||
return new CraftEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (player == null || card == null || card.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter() + 1) {
|
||||
return false;
|
||||
}
|
||||
game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE);
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -375,13 +375,20 @@ public final class StaticFilters {
|
|||
FILTER_CONTROLLED_A_PERMANENT.setLockedFilter(true);
|
||||
}
|
||||
|
||||
public static final FilterControlledPermanent FILTER_CONTROLLED_ANOTHER_PERMANENT = new FilterControlledPermanent("another target permanent you control");
|
||||
public static final FilterControlledPermanent FILTER_CONTROLLED_ANOTHER_PERMANENT = new FilterControlledPermanent("another permanent you control");
|
||||
|
||||
static {
|
||||
FILTER_CONTROLLED_ANOTHER_PERMANENT.add(AnotherPredicate.instance);
|
||||
FILTER_CONTROLLED_ANOTHER_PERMANENT.setLockedFilter(true);
|
||||
}
|
||||
|
||||
public static final FilterControlledPermanent FILTER_CONTROLLED_ANOTHER_TARGET_PERMANENT = new FilterControlledPermanent("another target permanent you control");
|
||||
|
||||
static {
|
||||
FILTER_CONTROLLED_ANOTHER_TARGET_PERMANENT.add(AnotherPredicate.instance);
|
||||
FILTER_CONTROLLED_ANOTHER_TARGET_PERMANENT.setLockedFilter(true);
|
||||
}
|
||||
|
||||
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENTS = new FilterControlledPermanent("permanents you control");
|
||||
|
||||
static {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,11 @@ public class TargetCardInGraveyardBattlefieldOrStack extends TargetCard {
|
|||
protected final FilterSpell filterSpell;
|
||||
|
||||
public TargetCardInGraveyardBattlefieldOrStack(int minNumTargets, int maxNumTargets, FilterCard filterGraveyard, FilterPermanent filterBattlefield) {
|
||||
this(minNumTargets, maxNumTargets, filterGraveyard, filterBattlefield, defaultSpellFilter, null);
|
||||
this(minNumTargets, maxNumTargets, filterGraveyard, filterBattlefield, null);
|
||||
}
|
||||
|
||||
public TargetCardInGraveyardBattlefieldOrStack(int minNumTargets, int maxNumTargets, FilterCard filterGraveyard, FilterPermanent filterBattlefield, String targetName) {
|
||||
this(minNumTargets, maxNumTargets, filterGraveyard, filterBattlefield, defaultSpellFilter, targetName);
|
||||
}
|
||||
|
||||
public TargetCardInGraveyardBattlefieldOrStack(int minNumTargets, int maxNumTargets, FilterCard filterGraveyard, FilterPermanent filterBattlefield, FilterSpell filterSpell, String targetName) {
|
||||
|
|
@ -40,7 +44,7 @@ public class TargetCardInGraveyardBattlefieldOrStack extends TargetCard {
|
|||
this.filterPermanent = filterBattlefield;
|
||||
this.filterSpell = filterSpell;
|
||||
this.targetName = targetName != null ? targetName : filter.getMessage()
|
||||
+ " in a graveyard "
|
||||
+ " in a graveyard"
|
||||
+ (maxNumTargets > 1 ? " and/or " : " or ")
|
||||
+ this.filterPermanent.getMessage()
|
||||
+ " on the battlefield";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue