mirror of
https://github.com/correl/mage.git
synced 2024-11-14 11:09:31 +00:00
[BRO] Implemented Gix, Yawgmoth Praetor (#9765)
* [BRO] Implemented Gix, Yawgmoth Praetor * Add missing TestPlayer, PlayerStub implementations
This commit is contained in:
parent
89687b305a
commit
92b6d7a531
8 changed files with 280 additions and 15 deletions
|
@ -2280,6 +2280,53 @@ public class HumanPlayer extends PlayerImpl {
|
|||
return card.getSpellAbility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivatedAbility chooseLandOrSpellAbility(Card card, Game game, boolean noMana) {
|
||||
if (gameInCheckPlayableState(game)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: add canRespond cycle?
|
||||
if (!canRespond()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MageObject object = game.getObject(card.getId()); // must be object to find real abilities (example: commander)
|
||||
if (object != null) {
|
||||
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = new LinkedHashMap<>(PlayerImpl.getCastableSpellAbilities(game, playerId, object, game.getState().getZone(object.getId()), noMana));
|
||||
|
||||
if (canPlayLand() && isActivePlayer(game)) {
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (ability instanceof PlayLandAbility) {
|
||||
useableAbilities.put(ability.getId(), (PlayLandAbility) ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (useableAbilities.size()) {
|
||||
case 0:
|
||||
return card.getSpellAbility();
|
||||
case 1:
|
||||
return useableAbilities.values().iterator().next();
|
||||
default:
|
||||
updateGameStatePriority("chooseLandOrSpellAbility", game);
|
||||
prepareForResponse(game);
|
||||
if (!isExecutingMacro()) {
|
||||
String message = "Choose spell or ability to play" + (noMana ? " for FREE" : "") + "<br>" + object.getLogName();
|
||||
game.fireGetChoiceEvent(playerId, message, object, new ArrayList<>(useableAbilities.values()));
|
||||
}
|
||||
waitForResponse(game);
|
||||
ActivatedAbility response = useableAbilities.get(getFixedResponseUUID(game));
|
||||
if (response != null) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default ability (example: on disconnect or cancel)
|
||||
return card.getSpellAbility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mode chooseMode(Modes modes, Ability source, Game game) {
|
||||
// choose mode to activate
|
||||
|
|
152
Mage.Sets/src/mage/cards/g/GixYawgmothPraetor.java
Normal file
152
Mage.Sets/src/mage/cards/g/GixYawgmothPraetor.java
Normal file
|
@ -0,0 +1,152 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.DiscardXTargetCost;
|
||||
import mage.abilities.costs.common.PayLifeCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.common.GetXValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.DrawCardTargetEffect;
|
||||
import mage.cards.*;
|
||||
import mage.constants.*;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetOpponent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author weirddan455
|
||||
*/
|
||||
public final class GixYawgmothPraetor extends CardImpl {
|
||||
|
||||
public GixYawgmothPraetor(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}");
|
||||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.PHYREXIAN);
|
||||
this.subtype.add(SubType.PRAETOR);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Whenever a creature deals combat damage to one of your opponents, its controller may pay 1 life. If they do, they draw a card.
|
||||
this.addAbility(new GixYawgmothPraetorTriggeredAbility());
|
||||
|
||||
// {4}{B}{B}{B}, Discard X cards: Exile the top X cards of target opponent's library. You may play land cards and cast spells from among cards exiled this way without paying their mana costs.
|
||||
Ability ability = new SimpleActivatedAbility(new GixYawgmothPraetorExileEffect(), new ManaCostsImpl<>("{4}{B}{B}{B}"));
|
||||
ability.addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS));
|
||||
ability.addTarget(new TargetOpponent());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private GixYawgmothPraetor(final GixYawgmothPraetor card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GixYawgmothPraetor copy() {
|
||||
return new GixYawgmothPraetor(this);
|
||||
}
|
||||
}
|
||||
|
||||
class GixYawgmothPraetorTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public GixYawgmothPraetorTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new GixYawgmothPraetorDrawEffect());
|
||||
setTriggerPhrase("Whenever a creature deals combat damage to one of your opponents, ");
|
||||
}
|
||||
|
||||
private GixYawgmothPraetorTriggeredAbility(final GixYawgmothPraetorTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GixYawgmothPraetorTriggeredAbility copy() {
|
||||
return new GixYawgmothPraetorTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
DamagedEvent damagedEvent = (DamagedEvent) event;
|
||||
if (damagedEvent.isCombatDamage() && game.getOpponents(controllerId).contains(damagedEvent.getTargetId())) {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(damagedEvent.getSourceId());
|
||||
if (permanent != null && permanent.isCreature(game)) {
|
||||
getEffects().setTargetPointer(new FixedTarget(permanent.getControllerId()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class GixYawgmothPraetorDrawEffect extends DoIfCostPaid {
|
||||
|
||||
public GixYawgmothPraetorDrawEffect() {
|
||||
super(new DrawCardTargetEffect(1), new PayLifeCost(1), "Pay 1 life and draw a card?");
|
||||
this.staticText = "its controller may pay 1 life. If they do, they draw a card";
|
||||
}
|
||||
|
||||
private GixYawgmothPraetorDrawEffect(final GixYawgmothPraetorDrawEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GixYawgmothPraetorDrawEffect copy() {
|
||||
return new GixYawgmothPraetorDrawEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Player getPayingPlayer(Game game, Ability source) {
|
||||
return game.getPlayer(targetPointer.getFirst(game, source));
|
||||
}
|
||||
}
|
||||
|
||||
class GixYawgmothPraetorExileEffect extends OneShotEffect {
|
||||
|
||||
public GixYawgmothPraetorExileEffect() {
|
||||
super(Outcome.PlayForFree);
|
||||
this.staticText = "Exile the top X cards of target opponent's library. You may play land cards and cast spells from among cards exiled this way without paying their mana costs.";
|
||||
}
|
||||
|
||||
private GixYawgmothPraetorExileEffect(final GixYawgmothPraetorExileEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GixYawgmothPraetorExileEffect copy() {
|
||||
return new GixYawgmothPraetorExileEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Player opponent = game.getPlayer(source.getFirstTarget());
|
||||
if (controller == null || opponent == null) {
|
||||
return false;
|
||||
}
|
||||
int xValue = GetXValue.instance.calculate(game, source, this);
|
||||
Set<Card> toExile = opponent.getLibrary().getTopCards(game, xValue);
|
||||
controller.moveCards(toExile, Zone.EXILED, source, game);
|
||||
Cards cards = new CardsImpl(toExile);
|
||||
cards.retainZone(Zone.EXILED, game);
|
||||
CardUtil.castMultipleWithAttributeForFree(controller, source, game, cards, StaticFilters.FILTER_CARD, Integer.MAX_VALUE, null, true);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -126,6 +126,7 @@ public final class TheBrothersWar extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Giant Growth", 183, Rarity.COMMON, mage.cards.g.GiantGrowth.class));
|
||||
cards.add(new SetCardInfo("Gix's Caress", 96, Rarity.COMMON, mage.cards.g.GixsCaress.class));
|
||||
cards.add(new SetCardInfo("Gix's Command", 97, Rarity.RARE, mage.cards.g.GixsCommand.class));
|
||||
cards.add(new SetCardInfo("Gix, Yawgmoth Praetor", 95, Rarity.MYTHIC, mage.cards.g.GixYawgmothPraetor.class));
|
||||
cards.add(new SetCardInfo("Gixian Infiltrator", 98, Rarity.COMMON, mage.cards.g.GixianInfiltrator.class));
|
||||
cards.add(new SetCardInfo("Gixian Puppeteer", 99, Rarity.RARE, mage.cards.g.GixianPuppeteer.class));
|
||||
cards.add(new SetCardInfo("Gixian Skullflayer", 100, Rarity.COMMON, mage.cards.g.GixianSkullflayer.class));
|
||||
|
|
|
@ -4387,6 +4387,40 @@ public class TestPlayer implements Player {
|
|||
return computerPlayer.chooseAbilityForCast(card, game, noMana);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivatedAbility chooseLandOrSpellAbility(Card card, Game game, boolean noMana) {
|
||||
assertAliasSupportInChoices(false);
|
||||
MageObject object = game.getObject(card.getId()); // must be object to find real abilities (example: commander)
|
||||
Map<UUID, ActivatedAbility> useable = new LinkedHashMap<>(PlayerImpl.getCastableSpellAbilities(game, this.getId(), object, game.getState().getZone(object.getId()), noMana));
|
||||
if (canPlayLand()) {
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (ability instanceof PlayLandAbility) {
|
||||
useable.put(ability.getId(), (PlayLandAbility) ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (useable.size() == 1) {
|
||||
return useable.values().iterator().next();
|
||||
}
|
||||
|
||||
if (!choices.isEmpty()) {
|
||||
for (ActivatedAbility ability : useable.values()) {
|
||||
if (ability.toString().startsWith(choices.get(0))) {
|
||||
choices.remove(0);
|
||||
return ability;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: enable fail checks and fix tests
|
||||
//Assert.fail("Wrong choice");
|
||||
LOGGER.warn("Wrong choice");
|
||||
}
|
||||
|
||||
String allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n"));
|
||||
this.chooseStrictModeFailed("choice", game, getInfo(card) + " - can't select ability to cast.\n" + "Card's abilities:\n" + allInfo);
|
||||
return computerPlayer.chooseAbilityForCast(card, game, noMana);
|
||||
}
|
||||
|
||||
public ComputerPlayer getComputerPlayer() {
|
||||
return computerPlayer;
|
||||
}
|
||||
|
|
|
@ -1429,4 +1429,8 @@ public class PlayerStub implements Player {
|
|||
return card.getSpellAbility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivatedAbility chooseLandOrSpellAbility(Card card, Game game, boolean noMana) {
|
||||
return card.getSpellAbility();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -425,6 +425,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*/
|
||||
SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana);
|
||||
|
||||
ActivatedAbility chooseLandOrSpellAbility(Card card, Game game, boolean noMana);
|
||||
|
||||
boolean removeFromHand(Card card, Game game);
|
||||
|
||||
boolean removeFromBattlefield(Permanent permanent, Ability source, Game game);
|
||||
|
|
|
@ -5127,6 +5127,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return this.phyrexianColors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivatedAbility chooseLandOrSpellAbility(Card card, Game game, boolean noMana) {
|
||||
return card.getSpellAbility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
|
||||
return card.getSpellAbility();
|
||||
|
|
|
@ -4,10 +4,7 @@ import com.google.common.collect.ImmutableList;
|
|||
import mage.ApprovingObject;
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.*;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
|
@ -1235,7 +1232,8 @@ public final class CardUtil {
|
|||
void addCard(Card card, Ability source, Game game);
|
||||
}
|
||||
|
||||
private static List<Card> getCastableComponents(Card cardToCast, FilterCard filter, Ability source, UUID playerId, Game game, SpellCastTracker spellCastTracker) {
|
||||
private static List<Card> getCastableComponents(Card cardToCast, FilterCard filter, Ability source, Player player, Game game, SpellCastTracker spellCastTracker, boolean playLand) {
|
||||
UUID playerId = player.getId();
|
||||
List<Card> cards = new ArrayList<>();
|
||||
if (cardToCast instanceof CardWithHalves) {
|
||||
cards.add(((CardWithHalves) cardToCast).getLeftHalfCard());
|
||||
|
@ -1247,7 +1245,9 @@ public final class CardUtil {
|
|||
cards.add(cardToCast);
|
||||
}
|
||||
cards.removeIf(Objects::isNull);
|
||||
cards.removeIf(card -> card.isLand(game));
|
||||
if (!playLand || !player.canPlayLand() || !game.isActivePlayer(playerId)) {
|
||||
cards.removeIf(card -> card.isLand(game));
|
||||
}
|
||||
cards.removeIf(card -> !filter.match(card, playerId, source, game));
|
||||
if (spellCastTracker != null) {
|
||||
cards.removeIf(card -> !spellCastTracker.checkCard(card, game));
|
||||
|
@ -1266,9 +1266,13 @@ public final class CardUtil {
|
|||
}
|
||||
|
||||
public static boolean castSpellWithAttributesForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter, SpellCastTracker spellCastTracker) {
|
||||
return castSpellWithAttributesForFree(player, source, game, cards, filter, spellCastTracker, false);
|
||||
}
|
||||
|
||||
public static boolean castSpellWithAttributesForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter, SpellCastTracker spellCastTracker, boolean playLand) {
|
||||
Map<UUID, List<Card>> cardMap = new HashMap<>();
|
||||
for (Card card : cards.getCards(game)) {
|
||||
List<Card> castableComponents = getCastableComponents(card, filter, source, player.getId(), game, spellCastTracker);
|
||||
List<Card> castableComponents = getCastableComponents(card, filter, source, player, game, spellCastTracker, playLand);
|
||||
if (!castableComponents.isEmpty()) {
|
||||
cardMap.put(card.getId(), castableComponents);
|
||||
}
|
||||
|
@ -1303,10 +1307,22 @@ public final class CardUtil {
|
|||
return false;
|
||||
}
|
||||
partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE));
|
||||
boolean result = player.cast(
|
||||
player.chooseAbilityForCast(cardToCast, game, true),
|
||||
game, true, new ApprovingObject(source, game)
|
||||
);
|
||||
ActivatedAbility chosenAbility;
|
||||
if (playLand) {
|
||||
chosenAbility = player.chooseLandOrSpellAbility(cardToCast, game, true);
|
||||
} else {
|
||||
chosenAbility = player.chooseAbilityForCast(cardToCast, game, true);
|
||||
}
|
||||
boolean result = false;
|
||||
if (chosenAbility instanceof SpellAbility) {
|
||||
result = player.cast(
|
||||
(SpellAbility) chosenAbility,
|
||||
game, true, new ApprovingObject(source, game)
|
||||
);
|
||||
} else if (playLand && chosenAbility instanceof PlayLandAbility) {
|
||||
Card land = game.getCard(chosenAbility.getSourceId());
|
||||
result = player.playLand(land, game, true);
|
||||
}
|
||||
partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null));
|
||||
if (result && spellCastTracker != null) {
|
||||
spellCastTracker.addCard(cardToCast, source, game);
|
||||
|
@ -1317,11 +1333,11 @@ public final class CardUtil {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static boolean checkForPlayable(Cards cards, FilterCard filter, Ability source, UUID playerId, Game game, SpellCastTracker spellCastTracker) {
|
||||
private static boolean checkForPlayable(Cards cards, FilterCard filter, Ability source, Player player, Game game, SpellCastTracker spellCastTracker, boolean playLand) {
|
||||
return cards
|
||||
.getCards(game)
|
||||
.stream()
|
||||
.anyMatch(card -> !getCastableComponents(card, filter, source, playerId, game, spellCastTracker).isEmpty());
|
||||
.anyMatch(card -> !getCastableComponents(card, filter, source, player, game, spellCastTracker, playLand).isEmpty());
|
||||
}
|
||||
|
||||
public static void castMultipleWithAttributeForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter) {
|
||||
|
@ -1333,6 +1349,10 @@ public final class CardUtil {
|
|||
}
|
||||
|
||||
public static void castMultipleWithAttributeForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter, int maxSpells, SpellCastTracker spellCastTracker) {
|
||||
castMultipleWithAttributeForFree(player, source, game, cards, filter, maxSpells, spellCastTracker, false);
|
||||
}
|
||||
|
||||
public static void castMultipleWithAttributeForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter, int maxSpells, SpellCastTracker spellCastTracker, boolean playLand) {
|
||||
if (maxSpells == 1) {
|
||||
CardUtil.castSpellWithAttributesForFree(player, source, game, cards, filter);
|
||||
return;
|
||||
|
@ -1340,11 +1360,11 @@ public final class CardUtil {
|
|||
int spellsCast = 0;
|
||||
cards.removeZone(Zone.STACK, game);
|
||||
while (player.canRespond() && spellsCast < maxSpells && !cards.isEmpty()) {
|
||||
if (CardUtil.castSpellWithAttributesForFree(player, source, game, cards, filter, spellCastTracker)) {
|
||||
if (CardUtil.castSpellWithAttributesForFree(player, source, game, cards, filter, spellCastTracker, playLand)) {
|
||||
spellsCast++;
|
||||
cards.removeZone(Zone.STACK, game);
|
||||
} else if (!checkForPlayable(
|
||||
cards, filter, source, player.getId(), game, spellCastTracker
|
||||
cards, filter, source, player, game, spellCastTracker, playLand
|
||||
) || !player.chooseUse(
|
||||
Outcome.PlayForFree, "Continue casting spells?", source, game
|
||||
)) {
|
||||
|
|
Loading…
Reference in a new issue