-
Notifications
You must be signed in to change notification settings - Fork 785
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
[STX] Implement Ecological Appreciation #7732
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package mage.cards.e; | ||
|
||
import mage.MageObject; | ||
import mage.abilities.Ability; | ||
import mage.abilities.effects.OneShotEffect; | ||
import mage.abilities.effects.common.ExileSpellEffect; | ||
import mage.cards.*; | ||
import mage.constants.CardType; | ||
import mage.constants.Outcome; | ||
import mage.constants.Zone; | ||
import mage.filter.FilterCard; | ||
import mage.filter.StaticFilters; | ||
import mage.filter.common.FilterCreatureCard; | ||
import mage.game.Game; | ||
import mage.players.Player; | ||
import mage.target.TargetCard; | ||
import mage.target.common.TargetOpponent; | ||
|
||
import java.util.UUID; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* | ||
* @author htrajan | ||
*/ | ||
public final class EcologicalAppreciation extends CardImpl { | ||
|
||
public EcologicalAppreciation(UUID ownerId, CardSetInfo setInfo) { | ||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{2}{G}"); | ||
|
||
// Search your library and graveyard for up to four creature cards with different names that each have mana value X or less and reveal them. An opponent chooses two of those cards. Shuffle the chosen cards into your library and put the rest onto the battlefield. Exile Ecological Appreciation. | ||
this.getSpellAbility().addEffect(new EcologicalAppreciationEffect()); | ||
this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); | ||
} | ||
|
||
private EcologicalAppreciation(final EcologicalAppreciation card) { | ||
super(card); | ||
} | ||
|
||
@Override | ||
public EcologicalAppreciation copy() { | ||
return new EcologicalAppreciation(this); | ||
} | ||
} | ||
|
||
class EcologicalAppreciationEffect extends OneShotEffect { | ||
|
||
EcologicalAppreciationEffect() { | ||
super(Outcome.Benefit); | ||
staticText = "search your library and graveyard for up to four creature cards with different names that each have mana value X or less and reveal them. An opponent chooses two of those cards. Shuffle the chosen cards into your library and put the rest onto the battlefield"; | ||
} | ||
|
||
private EcologicalAppreciationEffect(final EcologicalAppreciationEffect effect) { | ||
super(effect); | ||
} | ||
|
||
@Override | ||
public EcologicalAppreciationEffect copy() { | ||
return new EcologicalAppreciationEffect(this); | ||
} | ||
|
||
@Override | ||
public boolean apply(Game game, Ability source) { | ||
Player player = game.getPlayer(source.getControllerId()); | ||
if (player == null) { | ||
return false; | ||
} | ||
int xValue = source.getManaCostsToPay().getX(); | ||
TargetCard targetCardsInLibrary = new EcologicalAppreciationTarget(Zone.LIBRARY, 4, xValue); | ||
targetCardsInLibrary.setNotTarget(true); | ||
boolean searched = player.choose(Outcome.PutCreatureInPlay, new CardsImpl(player.getLibrary().getCards(game)), targetCardsInLibrary, game); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Simple xmage hint:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. understood 👍 |
||
Cards cards = new CardsImpl(targetCardsInLibrary.getTargets()); | ||
|
||
if (cards.isEmpty()) { | ||
if (searched) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a smell code to call shuffle three times. You can store it the status var and use it at the end. Also don't use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
player.shuffleLibrary(source, game); | ||
} | ||
return false; | ||
} | ||
|
||
int remainingCards = 4 - cards.size(); | ||
if (remainingCards > 0) { | ||
TargetCard targetCardsInGY = new EcologicalAppreciationTarget(Zone.GRAVEYARD, remainingCards, xValue); | ||
targetCardsInGY.setNotTarget(true); | ||
player.choose(Outcome.PutCreatureInPlay, new CardsImpl(player.getGraveyard().getCards(game)), targetCardsInGY, game); | ||
cards.addAll(targetCardsInGY.getTargets()); | ||
} | ||
|
||
TargetOpponent targetOpponent = new TargetOpponent(); | ||
targetOpponent.setNotTarget(true); | ||
player.choose(outcome, targetOpponent, source.getSourceId(), game); | ||
Player opponent = game.getPlayer(targetOpponent.getFirstTarget()); | ||
if (opponent == null) { | ||
if (searched) { | ||
player.shuffleLibrary(source, game); | ||
} | ||
return false; | ||
} | ||
|
||
TargetCard chosenCards = new TargetCard(2, Zone.ALL, StaticFilters.FILTER_CARD); | ||
chosenCards.setNotTarget(true); | ||
opponent.choose(outcome, cards, chosenCards, game); | ||
Cards toShuffle = new CardsImpl(chosenCards.getTargets().stream() | ||
.map(game::getCard) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing filter by Objects::nonNull There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
.collect(Collectors.toList())); | ||
|
||
player.putCardsOnTopOfLibrary(toShuffle, game, source, false); | ||
player.shuffleLibrary(source, game); | ||
cards.removeAll(toShuffle); | ||
|
||
player.moveCards(cards, Zone.BATTLEFIELD, source, game); | ||
|
||
return true; | ||
} | ||
} | ||
|
||
class EcologicalAppreciationTarget extends TargetCard { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not extends It's important cause AI uses only known target type and will raise error on the new one (it's not critical in your use case cause it uses existing cards list but in can be critical in another custom targets). I recommends to use name and cmc predicates to generate dynamic filter and use it in choose dialogs. If you can't then keep that code (it's ok in your use case). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Create new filter on apply ( Name predicate usage example:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. But I can see the problem with predicates now -- you still allows to select same names in one step, so |
||
|
||
private static final FilterCard filter = new FilterCreatureCard("creature cards with different names that each have mana value X or less"); | ||
|
||
private final int xValue; | ||
|
||
EcologicalAppreciationTarget(Zone zone, int maxTargets, int xValue) { | ||
super(0, maxTargets, zone, filter); | ||
this.xValue = xValue; | ||
} | ||
|
||
private EcologicalAppreciationTarget(final EcologicalAppreciationTarget target) { | ||
super(target); | ||
this.xValue = target.xValue; | ||
} | ||
|
||
@Override | ||
public EcologicalAppreciationTarget copy() { | ||
return new EcologicalAppreciationTarget(this); | ||
} | ||
|
||
@Override | ||
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { | ||
if (!super.canTarget(playerId, id, source, game)) { | ||
return false; | ||
} | ||
Card card = game.getCard(id); | ||
return card != null && card.getConvertedManaCost() <= xValue && | ||
this.getTargets().stream() | ||
.map(game::getCard) | ||
.map(MageObject::getName) | ||
.noneMatch(card.getName()::equals); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you need names compare then use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use
target.withChooseHint
to inform user in choose dialog about current step or additional info. Example: "step 1 of 2, from library".There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added