mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
Commanders improves:
* [KHM] fixed that some effects can't find mdf commanders on battlefield (example: Fierce Guardianship, #7504); * Oathbreaker: fixed that some cards that refer to commander can affects signature spells too;
This commit is contained in:
parent
62cad8e850
commit
dc0a29007c
34 changed files with 293 additions and 199 deletions
|
|
@ -117,12 +117,12 @@ public class OathbreakerFreeForAll extends GameCommanderImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> getCommandersIds(Player player, CommanderCardType commanderCardType) {
|
public Set<UUID> getCommandersIds(Player player, CommanderCardType commanderCardType, boolean returnAllCardParts) {
|
||||||
Set<UUID> res = new HashSet<>();
|
Set<UUID> res = new HashSet<>();
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
Set<UUID> commanders = this.playerOathbreakers.getOrDefault(player.getId(), new HashSet<>());
|
Set<UUID> commanders = this.playerOathbreakers.getOrDefault(player.getId(), new HashSet<>());
|
||||||
Set<UUID> spells = this.playerSignatureSpells.getOrDefault(player.getId(), new HashSet<>());
|
Set<UUID> spells = this.playerSignatureSpells.getOrDefault(player.getId(), new HashSet<>());
|
||||||
for (UUID commanderId : super.getCommandersIds(player, commanderCardType)) {
|
for (UUID commanderId : super.getCommandersIds(player, commanderCardType, returnAllCardParts)) {
|
||||||
switch (commanderCardType) {
|
switch (commanderCardType) {
|
||||||
case ANY:
|
case ANY:
|
||||||
res.add(commanderId);
|
res.add(commanderId);
|
||||||
|
|
@ -133,6 +133,7 @@ public class OathbreakerFreeForAll extends GameCommanderImpl {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SIGNATURE_SPELL:
|
case SIGNATURE_SPELL:
|
||||||
|
// TODO: doesn't filter mdf cards with different sides (creature + spell)
|
||||||
if (spells.contains(commanderId)) {
|
if (spells.contains(commanderId)) {
|
||||||
res.add(commanderId);
|
res.add(commanderId);
|
||||||
}
|
}
|
||||||
|
|
@ -142,6 +143,6 @@ public class OathbreakerFreeForAll extends GameCommanderImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return super.filterCommandersBySearchZone(res, returnAllCardParts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ enum CaptainVargusWrathValue implements DynamicValue {
|
||||||
}
|
}
|
||||||
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||||
return watcher == null ? 0 : game
|
return watcher == null ? 0 : game
|
||||||
.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER)
|
.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, false)
|
||||||
.stream()
|
.stream()
|
||||||
.mapToInt(watcher::getPlaysCount)
|
.mapToInt(watcher::getPlaysCount)
|
||||||
.sum();
|
.sum();
|
||||||
|
|
|
||||||
|
|
@ -66,13 +66,7 @@ class CommandBeaconEffect extends OneShotEffect {
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
List<Card> commandersInCommandZone = new ArrayList<>(1);
|
List<Card> commandersInCommandZone = new ArrayList<>(game.getCommanderCardsFromCommandZone(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER));
|
||||||
for (UUID commanderId : game.getCommandersIds(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER)) {
|
|
||||||
Card commander = game.getCard(commanderId);
|
|
||||||
if (commander != null && game.getState().getZone(commander.getId()) == Zone.COMMAND) {
|
|
||||||
commandersInCommandZone.add(commander);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (commandersInCommandZone.size() == 1) {
|
if (commandersInCommandZone.size() == 1) {
|
||||||
controller.moveCards(commandersInCommandZone.get(0), Zone.HAND, source, game);
|
controller.moveCards(commandersInCommandZone.get(0), Zone.HAND, source, game);
|
||||||
} else if (commandersInCommandZone.size() == 2) {
|
} else if (commandersInCommandZone.size() == 2) {
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ enum CommandersInsigniaValue implements DynamicValue {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return game
|
return game
|
||||||
.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER)
|
.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, false)
|
||||||
.stream()
|
.stream()
|
||||||
.mapToInt(watcher::getPlaysCount)
|
.mapToInt(watcher::getPlaysCount)
|
||||||
.sum();
|
.sum();
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ class CommandersPlateEffect extends ContinuousEffectImpl {
|
||||||
permanent = game.getPermanentOrLKIBattlefield(equipment.getAttachedTo());
|
permanent = game.getPermanentOrLKIBattlefield(equipment.getAttachedTo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Set<UUID> commanders = game.getCommandersIds(player);
|
Set<UUID> commanders = game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, false);
|
||||||
if (commanders.isEmpty()) {
|
if (commanders.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
package mage.cards.g;
|
package mage.cards.g;
|
||||||
|
|
||||||
|
import mage.ApprovingObject;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
|
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
|
||||||
import mage.abilities.costs.mana.ManaCost;
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.keyword.TrampleAbility;
|
import mage.abilities.keyword.TrampleAbility;
|
||||||
import mage.cards.*;
|
import mage.cards.Card;
|
||||||
import mage.constants.CardType;
|
import mage.cards.CardImpl;
|
||||||
import mage.constants.Outcome;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.SubType;
|
import mage.cards.CardsImpl;
|
||||||
import mage.constants.Zone;
|
import mage.constants.*;
|
||||||
import mage.filter.FilterCard;
|
import mage.filter.FilterCard;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
@ -18,10 +19,8 @@ import mage.target.TargetCard;
|
||||||
import mage.util.ManaUtil;
|
import mage.util.ManaUtil;
|
||||||
import mage.watchers.common.CommanderPlaysCountWatcher;
|
import mage.watchers.common.CommanderPlaysCountWatcher;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.ApprovingObject;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author spjspj, JayDi85
|
* @author spjspj, JayDi85
|
||||||
|
|
@ -69,36 +68,31 @@ class GeodeGolemEffect extends OneShotEffect {
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
UUID selectedCommanderId = null;
|
Card selectedCommander = null;
|
||||||
Set<UUID> possibleCommanders = new HashSet<>();
|
|
||||||
for (UUID id : game.getCommandersIds(controller)) {
|
|
||||||
if (game.getState().getZone(id) == Zone.COMMAND) {
|
|
||||||
possibleCommanders.add(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (possibleCommanders.isEmpty()) {
|
Set<Card> commandersInCommandZone = game.getCommanderCardsFromCommandZone(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER);
|
||||||
|
if (commandersInCommandZone.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// select from commanders
|
// select from commanders
|
||||||
if (possibleCommanders.size() == 1) {
|
if (commandersInCommandZone.size() == 1) {
|
||||||
selectedCommanderId = possibleCommanders.iterator().next();
|
selectedCommander = commandersInCommandZone.stream().findFirst().get();
|
||||||
} else {
|
} else {
|
||||||
TargetCard target = new TargetCard(Zone.COMMAND, new FilterCard(
|
TargetCard target = new TargetCard(Zone.COMMAND, new FilterCard("commander to cast without mana cost"));
|
||||||
"commander to cast without mana cost"));
|
|
||||||
Cards cards = new CardsImpl(possibleCommanders);
|
|
||||||
target.setNotTarget(true);
|
target.setNotTarget(true);
|
||||||
if (controller.canRespond()
|
if (controller.canRespond()
|
||||||
&& controller.choose(Outcome.PlayForFree, cards, target, game)) {
|
&& controller.choose(Outcome.PlayForFree, new CardsImpl(commandersInCommandZone), target, game)) {
|
||||||
if (target.getFirstTarget() != null) {
|
if (target.getFirstTarget() != null) {
|
||||||
selectedCommanderId = target.getFirstTarget();
|
selectedCommander = commandersInCommandZone.stream()
|
||||||
|
.filter(c -> c.getId().equals(target.getFirstTarget()))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Card commander = game.getCard(selectedCommanderId);
|
if (selectedCommander == null) {
|
||||||
if (commander == null) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +100,7 @@ class GeodeGolemEffect extends OneShotEffect {
|
||||||
// TODO: this is broken with the commander cost reduction effect
|
// TODO: this is broken with the commander cost reduction effect
|
||||||
ManaCost cost = null;
|
ManaCost cost = null;
|
||||||
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||||
int castCount = watcher.getPlaysCount(commander.getId());
|
int castCount = watcher.getPlaysCount(selectedCommander.getId());
|
||||||
if (castCount > 0) {
|
if (castCount > 0) {
|
||||||
cost = ManaUtil.createManaCost(castCount * 2, false);
|
cost = ManaUtil.createManaCost(castCount * 2, false);
|
||||||
}
|
}
|
||||||
|
|
@ -114,14 +108,14 @@ class GeodeGolemEffect extends OneShotEffect {
|
||||||
// CAST: as spell or as land
|
// CAST: as spell or as land
|
||||||
if (cost == null
|
if (cost == null
|
||||||
|| cost.pay(source, game, source, controller.getId(), false, null)) {
|
|| cost.pay(source, game, source, controller.getId(), false, null)) {
|
||||||
if (commander.getSpellAbility() != null) {
|
if (selectedCommander.getSpellAbility() != null) { // TODO: can be broken with mdf cards (one side creature, one side land)?
|
||||||
game.getState().setValue("PlayFromNotOwnHandZone" + commander.getId(), Boolean.TRUE);
|
game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), Boolean.TRUE);
|
||||||
Boolean commanderWasCast = controller.cast(controller.chooseAbilityForCast(commander, game, true),
|
Boolean commanderWasCast = controller.cast(controller.chooseAbilityForCast(selectedCommander, game, true),
|
||||||
game, true, new ApprovingObject(source, game));
|
game, true, new ApprovingObject(source, game));
|
||||||
game.getState().setValue("PlayFromNotOwnHandZone" + commander.getId(), null);
|
game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), null);
|
||||||
return commanderWasCast;
|
return commanderWasCast;
|
||||||
} else {
|
} else {
|
||||||
return controller.playLand(commander, game, true);
|
return controller.playLand(selectedCommander, game, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ enum JeskaThriceRebornValue implements DynamicValue {
|
||||||
}
|
}
|
||||||
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||||
return watcher == null ? 0 : game
|
return watcher == null ? 0 : game
|
||||||
.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER)
|
.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, false)
|
||||||
.stream()
|
.stream()
|
||||||
.mapToInt(watcher::getPlaysCount)
|
.mapToInt(watcher::getPlaysCount)
|
||||||
.sum();
|
.sum();
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import mage.game.Game;
|
||||||
import mage.game.GameImpl;
|
import mage.game.GameImpl;
|
||||||
import mage.game.command.Commander;
|
import mage.game.command.Commander;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.events.GameEvent.EventType;
|
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.game.permanent.PermanentImpl;
|
import mage.game.permanent.PermanentImpl;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
@ -109,7 +108,7 @@ class KarnLiberatedEffect extends OneShotEffect {
|
||||||
if (card.isOwnedBy(player.getId()) && !card.isCopy() // no copies
|
if (card.isOwnedBy(player.getId()) && !card.isCopy() // no copies
|
||||||
&& !player.getSideboard().contains(card.getId())
|
&& !player.getSideboard().contains(card.getId())
|
||||||
&& !cards.contains(card)) { // not the exiled cards
|
&& !cards.contains(card)) { // not the exiled cards
|
||||||
if (game.getCommandersIds(player).contains(card.getId())) {
|
if (game.getCommandersIds(player, CommanderCardType.ANY, false).contains(card.getId())) {
|
||||||
game.addCommander(new Commander(card)); // TODO: check restart and init
|
game.addCommander(new Commander(card)); // TODO: check restart and init
|
||||||
// no needs in initCommander call -- it's used on game startup (init)
|
// no needs in initCommander call -- it's used on game startup (init)
|
||||||
game.setZone(card.getId(), Zone.COMMAND);
|
game.setZone(card.getId(), Zone.COMMAND);
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,8 @@ class MythUnboundCostReductionEffect extends CostModificationEffectImpl {
|
||||||
|
|
||||||
if (abilityToModify instanceof SpellAbility || abilityToModify instanceof PlayLandAbility) {
|
if (abilityToModify instanceof SpellAbility || abilityToModify instanceof PlayLandAbility) {
|
||||||
if (abilityToModify.isControlledBy(source.getControllerId())) {
|
if (abilityToModify.isControlledBy(source.getControllerId())) {
|
||||||
return game.getCommandersIds(player).contains(abilityToModify.getSourceId());
|
// must check all card parts (example: mdf commander)
|
||||||
|
return game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, true).contains(abilityToModify.getSourceId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@ import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author TheElk801
|
* @author TheElk801
|
||||||
|
|
@ -68,13 +67,8 @@ class NetherbornAltarEffect extends OneShotEffect {
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<Card> commandersInCommandZone = game
|
|
||||||
.getCommandersIds(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER)
|
List<Card> commandersInCommandZone = new ArrayList<>(game.getCommanderCardsFromCommandZone(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER));
|
||||||
.stream()
|
|
||||||
.map(game::getCard)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.filter(commander -> game.getState().getZone(commander.getId()) == Zone.COMMAND)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
if (commandersInCommandZone.size() == 1) {
|
if (commandersInCommandZone.size() == 1) {
|
||||||
controller.moveCards(commandersInCommandZone.get(0), Zone.HAND, source, game);
|
controller.moveCards(commandersInCommandZone.get(0), Zone.HAND, source, game);
|
||||||
} else if (commandersInCommandZone.size() == 2) {
|
} else if (commandersInCommandZone.size() == 2) {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ public final class OpalPalace extends CardImpl {
|
||||||
|
|
||||||
class OpalPalaceWatcher extends Watcher {
|
class OpalPalaceWatcher extends Watcher {
|
||||||
|
|
||||||
private List<UUID> commanderId = new ArrayList<>();
|
private final List<UUID> commanderPartsId = new ArrayList<>();
|
||||||
private final String originalId;
|
private final String originalId;
|
||||||
|
|
||||||
public OpalPalaceWatcher(String originalId) {
|
public OpalPalaceWatcher(String originalId) {
|
||||||
|
|
@ -66,8 +66,8 @@ class OpalPalaceWatcher extends Watcher {
|
||||||
this.originalId = originalId;
|
this.originalId = originalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean manaUsedToCastCommander(UUID id){
|
public boolean manaUsedToCastCommanderPart(UUID id) {
|
||||||
return commanderId.contains(id);
|
return commanderPartsId.contains(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -81,8 +81,9 @@ class OpalPalaceWatcher extends Watcher {
|
||||||
for (UUID playerId : game.getPlayerList()) {
|
for (UUID playerId : game.getPlayerList()) {
|
||||||
Player player = game.getPlayer(playerId);
|
Player player = game.getPlayer(playerId);
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
if (game.getCommandersIds(player).contains(card.getId())) {
|
// need check all card parts (example: mdf cards)
|
||||||
commanderId.add(card.getId());
|
if (game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, true).contains(card.getId())) {
|
||||||
|
commanderPartsId.add(card.getId());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +97,7 @@ class OpalPalaceWatcher extends Watcher {
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
super.reset();
|
super.reset();
|
||||||
commanderId.clear();
|
commanderPartsId.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,7 +120,7 @@ class OpalPalaceEntersBattlefieldEffect extends ReplacementEffectImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||||
OpalPalaceWatcher watcher = game.getState().getWatcher(OpalPalaceWatcher.class, source.getSourceId());
|
OpalPalaceWatcher watcher = game.getState().getWatcher(OpalPalaceWatcher.class, source.getSourceId());
|
||||||
return watcher != null && watcher.manaUsedToCastCommander(event.getTargetId());
|
return watcher != null && watcher.manaUsedToCastCommanderPart(event.getTargetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import mage.cards.Card;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.CommanderCardType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
|
|
@ -90,14 +91,9 @@ class PathOfAncestryTriggeredAbility extends DelayedTriggeredAbility {
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (UUID commanderId : game.getCommandersIds(player)) {
|
|
||||||
Card commander = game.getPermanent(commanderId);
|
// share creature type with commander
|
||||||
if (commander == null) {
|
for (Card commander : game.getCommanderCardsFromAnyZones(player, CommanderCardType.COMMANDER_OR_OATHBREAKER)) {
|
||||||
commander = game.getCard(commanderId);
|
|
||||||
}
|
|
||||||
if (commander == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (spell.getCard().shareCreatureTypes(game, commander)) {
|
if (spell.getCard().shareCreatureTypes(game, commander)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,15 +76,7 @@ class RoadOfReturnEffect extends OneShotEffect {
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<Card> commandersInCommandZone = new ArrayList<>(1);
|
List<Card> commandersInCommandZone = new ArrayList<>(game.getCommanderCardsFromCommandZone(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER));
|
||||||
game.getCommandersIds(
|
|
||||||
controller, CommanderCardType.COMMANDER_OR_OATHBREAKER
|
|
||||||
).stream().forEach(commanderId -> {
|
|
||||||
Card commander = game.getCard(commanderId);
|
|
||||||
if (commander != null && game.getState().getZone(commander.getId()) == Zone.COMMAND) {
|
|
||||||
commandersInCommandZone.add(commander);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (commandersInCommandZone.size() == 1) {
|
if (commandersInCommandZone.size() == 1) {
|
||||||
controller.moveCards(commandersInCommandZone.get(0), Zone.HAND, source, game);
|
controller.moveCards(commandersInCommandZone.get(0), Zone.HAND, source, game);
|
||||||
} else if (commandersInCommandZone.size() == 2) {
|
} else if (commandersInCommandZone.size() == 2) {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import mage.constants.Zone;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -62,9 +63,17 @@ class SkyfirePhoenixTriggeredAbility extends SpellCastControllerTriggeredAbility
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkTrigger(GameEvent event, Game game) {
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
return super.checkTrigger(event, game) && game.getCommandersIds(
|
if (!super.checkTrigger(event, game)) {
|
||||||
game.getPlayer(getControllerId()), CommanderCardType.COMMANDER_OR_OATHBREAKER
|
return false;
|
||||||
).contains(event.getSourceId());
|
}
|
||||||
|
|
||||||
|
Player controller = game.getPlayer(getControllerId());
|
||||||
|
if (controller == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// must check all parts (example: cast one from from mdf/split card)
|
||||||
|
return game.getCommandersIds(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER, true).contains(event.getSourceId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,8 @@ class TeferiMageOfZhalfirAddFlashEffect extends ContinuousEffectImpl {
|
||||||
game.getState().addOtherAbility(card, FlashAbility.getInstance());
|
game.getState().addOtherAbility(card, FlashAbility.getInstance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// commander in command zone
|
// cards in command zone
|
||||||
game.getCommanderCardsFromCommandZone(controller).stream()
|
game.getCommanderCardsFromCommandZone(controller, CommanderCardType.ANY).stream()
|
||||||
.filter(MageObject::isCreature)
|
.filter(MageObject::isCreature)
|
||||||
.forEach(card -> {
|
.forEach(card -> {
|
||||||
game.getState().addOtherAbility(card, FlashAbility.getInstance());
|
game.getState().addOtherAbility(card, FlashAbility.getInstance());
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
|
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
|
||||||
import mage.abilities.keyword.PartnerAbility;
|
import mage.abilities.keyword.PartnerAbility;
|
||||||
|
import mage.cards.Card;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.cards.CardsImpl;
|
import mage.cards.CardsImpl;
|
||||||
|
|
@ -23,11 +24,10 @@ import mage.players.Player;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.targetpointer.FixedTarget;
|
import mage.target.targetpointer.FixedTarget;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static mage.constants.Outcome.Benefit;
|
import static mage.constants.Outcome.Benefit;
|
||||||
|
|
||||||
|
|
@ -110,10 +110,12 @@ class TeveshSzatDoomOfFoolsSacrificeEffect extends OneShotEffect {
|
||||||
if (permanent == null) {
|
if (permanent == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean isCommander = game.getCommandersIds(
|
|
||||||
game.getPlayer(permanent.getControllerId()),
|
// must check all card parts (example: mdf commander)
|
||||||
CommanderCardType.COMMANDER_OR_OATHBREAKER
|
Player permanentController = game.getPlayer(permanent.getControllerId());
|
||||||
).contains(permanent.getId());
|
boolean isCommander = permanentController != null
|
||||||
|
&& game.getCommandersIds(permanentController, CommanderCardType.COMMANDER_OR_OATHBREAKER, true).contains(permanent.getId());
|
||||||
|
|
||||||
if (!permanent.sacrifice(source, game)) {
|
if (!permanent.sacrifice(source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -154,6 +156,8 @@ class TeveshSzatDoomOfFoolsCommanderEffect extends OneShotEffect {
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gain control of all commanders
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(
|
||||||
filter, source.getControllerId(), source.getSourceId(), game
|
filter, source.getControllerId(), source.getSourceId(), game
|
||||||
)) {
|
)) {
|
||||||
|
|
@ -161,16 +165,17 @@ class TeveshSzatDoomOfFoolsCommanderEffect extends OneShotEffect {
|
||||||
Duration.Custom, true
|
Duration.Custom, true
|
||||||
).setTargetPointer(new FixedTarget(permanent, game)), source);
|
).setTargetPointer(new FixedTarget(permanent, game)), source);
|
||||||
}
|
}
|
||||||
Set<UUID> commanders = game
|
|
||||||
.getPlayerList()
|
// put all commanders to battlefield under control
|
||||||
.stream()
|
// TODO: doesn't support range of influence (e.g. take control of all commanders)
|
||||||
|
Set<Card> commandersToPut = new HashSet<>();
|
||||||
|
game.getPlayerList().stream()
|
||||||
.map(game::getPlayer)
|
.map(game::getPlayer)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(player -> game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER))
|
.forEach(player -> {
|
||||||
.flatMap(Collection::stream)
|
commandersToPut.addAll(game.getCommanderCardsFromCommandZone(player, CommanderCardType.COMMANDER_OR_OATHBREAKER));
|
||||||
.filter(uuid -> game.getState().getZone(uuid) == Zone.COMMAND)
|
});
|
||||||
.collect(Collectors.toSet());
|
controller.moveCards(new CardsImpl(commandersToPut), Zone.BATTLEFIELD, source, game);
|
||||||
controller.moveCards(new CardsImpl(commanders), Zone.BATTLEFIELD, source, game);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import mage.cards.Card;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.CommanderCardType;
|
||||||
import mage.filter.FilterMana;
|
import mage.filter.FilterMana;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
@ -64,7 +65,7 @@ enum WarRoomValue implements DynamicValue {
|
||||||
ObjectColor color = new ObjectColor();
|
ObjectColor color = new ObjectColor();
|
||||||
// if no commander then cost can't be paid
|
// if no commander then cost can't be paid
|
||||||
boolean hasCommander = false;
|
boolean hasCommander = false;
|
||||||
for (UUID commanderId : game.getCommandersIds(controller)) {
|
for (UUID commanderId : game.getCommandersIds(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER, false)) {
|
||||||
Card commander = game.getCard(commanderId);
|
Card commander = game.getCard(commanderId);
|
||||||
if (commander == null) {
|
if (commander == null) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.continuous;
|
||||||
import mage.abilities.dynamicvalue.common.CommanderCastCountValue;
|
import mage.abilities.dynamicvalue.common.CommanderCastCountValue;
|
||||||
import mage.abilities.keyword.FirstStrikeAbility;
|
import mage.abilities.keyword.FirstStrikeAbility;
|
||||||
import mage.cards.AdventureCard;
|
import mage.cards.AdventureCard;
|
||||||
|
import mage.constants.CommanderCardType;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
|
@ -448,7 +449,7 @@ public class CommandersCastTest extends CardTestCommander4Players {
|
||||||
// can't cast adventure spell for {G} + {2} + {2}
|
// can't cast adventure spell for {G} + {2} + {2}
|
||||||
// can't cast creature spell for {G}{G} + {2} + {2}
|
// can't cast creature spell for {G}{G} + {2} + {2}
|
||||||
runCode("check commander tax 2x", 9, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
runCode("check commander tax 2x", 9, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||||
AdventureCard card = (AdventureCard) game.getCommanderCardsFromCommandZone(player).stream().findFirst().get();
|
AdventureCard card = (AdventureCard) game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY).stream().findFirst().get();
|
||||||
Assert.assertEquals(2, CommanderCastCountValue.instance.calculate(game, card.getSpellAbility(), null));
|
Assert.assertEquals(2, CommanderCastCountValue.instance.calculate(game, card.getSpellAbility(), null));
|
||||||
Assert.assertEquals(2, CommanderCastCountValue.instance.calculate(game, card.getSpellCard().getSpellAbility(), null));
|
Assert.assertEquals(2, CommanderCastCountValue.instance.calculate(game, card.getSpellCard().getSpellAbility(), null));
|
||||||
});
|
});
|
||||||
|
|
@ -474,7 +475,7 @@ public class CommandersCastTest extends CardTestCommander4Players {
|
||||||
checkPermanentCount("after last cast", 13, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair", 1);
|
checkPermanentCount("after last cast", 13, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair", 1);
|
||||||
checkPermanentTapped("after last cast", 13, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest", true, 2 + 2 + 2);
|
checkPermanentTapped("after last cast", 13, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest", true, 2 + 2 + 2);
|
||||||
runCode("check commander tax 3x", 13, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
runCode("check commander tax 3x", 13, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||||
AdventureCard card = (AdventureCard) game.getCard(game.getCommandersIds(player).stream().findFirst().get());
|
AdventureCard card = (AdventureCard) game.getCard(game.getCommandersIds(player, CommanderCardType.ANY, false).stream().findFirst().get());
|
||||||
Assert.assertEquals(3, CommanderCastCountValue.instance.calculate(game, card.getSpellAbility(), null));
|
Assert.assertEquals(3, CommanderCastCountValue.instance.calculate(game, card.getSpellAbility(), null));
|
||||||
Assert.assertEquals(3, CommanderCastCountValue.instance.calculate(game, card.getSpellCard().getSpellAbility(), null));
|
Assert.assertEquals(3, CommanderCastCountValue.instance.calculate(game, card.getSpellCard().getSpellAbility(), null));
|
||||||
});
|
});
|
||||||
|
|
@ -486,7 +487,7 @@ public class CommandersCastTest extends CardTestCommander4Players {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_ModalDoubleFacesCard() {
|
public void test_ModalDoubleFacesCard_1() {
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
// use case:
|
// use case:
|
||||||
|
|
@ -540,4 +541,41 @@ public class CommandersCastTest extends CardTestCommander4Players {
|
||||||
execute();
|
execute();
|
||||||
assertAllCommandsUsed();
|
assertAllCommandsUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ModalDoubleFacesCard_CanReturnAfterKillAndCommanderControlCondition() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
|
// Cosima, God of the Voyage, {2}{U}, creature, 2/4
|
||||||
|
// The Omenkeel, {1}{U}, artifact, vehicle
|
||||||
|
addCard(Zone.COMMAND, playerA, "Cosima, God of the Voyage", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||||
|
//
|
||||||
|
addCard(Zone.HAND, playerA, "Lightning Bolt", 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||||
|
//
|
||||||
|
// If you control a commander, you may cast this spell without paying its mana cost.
|
||||||
|
// Counter target noncreature spell.
|
||||||
|
addCard(Zone.HAND, playerA, "Fierce Guardianship", 1); // {2}{U}
|
||||||
|
|
||||||
|
// prepare commander on battlefield
|
||||||
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 3);
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cosima, God of the Voyage");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cosima, God of the Voyage", 1);
|
||||||
|
|
||||||
|
// kill and return to command zone
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Cosima, God of the Voyage");
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Cosima, God of the Voyage");
|
||||||
|
// check what commander control condition works with mdf parts
|
||||||
|
checkStackSize("must have 2 bolts on stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2);
|
||||||
|
checkPlayableAbility("must see commander on battle for free cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Fierce Guardianship", true);
|
||||||
|
//
|
||||||
|
setChoice(playerA, "Yes"); // return to command zone after kill
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -943,7 +943,7 @@ public class TestPlayer implements Player {
|
||||||
// show command
|
// show command
|
||||||
if (params[0].equals(SHOW_COMMAND_COMMAND) && params.length == 1) {
|
if (params[0].equals(SHOW_COMMAND_COMMAND) && params.length == 1) {
|
||||||
printStart(action.getActionName());
|
printStart(action.getActionName());
|
||||||
CardsImpl cards = new CardsImpl(game.getCommandersIds(computerPlayer));
|
CardsImpl cards = new CardsImpl(game.getCommandersIds(computerPlayer, CommanderCardType.ANY, false));
|
||||||
printCards(cards.getCards(game));
|
printCards(cards.getCards(game));
|
||||||
printEnd();
|
printEnd();
|
||||||
actions.remove(action);
|
actions.remove(action);
|
||||||
|
|
@ -1421,7 +1421,7 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
private void assertCommandCardCount(PlayerAction action, Game game, Player player, String cardName, int count) {
|
private void assertCommandCardCount(PlayerAction action, Game game, Player player, String cardName, int count) {
|
||||||
int realCount = 0;
|
int realCount = 0;
|
||||||
for (UUID cardId : game.getCommandersIds(player)) {
|
for (UUID cardId : game.getCommandersIds(player, CommanderCardType.ANY, false)) {
|
||||||
Card card = game.getCard(cardId);
|
Card card = game.getCard(cardId);
|
||||||
if (hasObjectTargetNameOrAlias(card, cardName) && Zone.COMMAND.equals(game.getState().getZone(cardId))) {
|
if (hasObjectTargetNameOrAlias(card, cardName) && Zone.COMMAND.equals(game.getState().getZone(cardId))) {
|
||||||
realCount++;
|
realCount++;
|
||||||
|
|
@ -1430,7 +1430,7 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
if (realCount != count) {
|
if (realCount != count) {
|
||||||
printStart("Cards in command zone from " + player.getName());
|
printStart("Cards in command zone from " + player.getName());
|
||||||
printCards(game.getCommanderCardsFromCommandZone(player));
|
printCards(game.getCommanderCardsFromCommandZone(player, CommanderCardType.COMMANDER_OR_OATHBREAKER));
|
||||||
printEnd();
|
printEnd();
|
||||||
Assert.fail(action.getActionName() + " - must have " + count + " cards with name " + cardName + ", but found " + realCount);
|
Assert.fail(action.getActionName() + " - must have " + count + " cards with name " + cardName + ", but found " + realCount);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,7 @@ package mage.abilities.condition.common;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.condition.Condition;
|
import mage.abilities.condition.Condition;
|
||||||
import mage.constants.CommanderCardType;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
|
||||||
import mage.players.Player;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the player has its commander in play and controls it
|
* Checks if the player has its commander in play and controls it
|
||||||
|
|
@ -20,16 +15,7 @@ public enum CommanderInPlayCondition implements Condition {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
return ControlACommanderCondition.instance.apply(game, source);
|
||||||
if (controller != null) {
|
|
||||||
for (UUID commanderId : game.getCommandersIds(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER)) {
|
|
||||||
Permanent commander = game.getPermanent(commanderId);
|
|
||||||
if (commander != null && commander.isControlledBy(source.getControllerId())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ public enum ControlACommanderCondition implements Condition {
|
||||||
.stream()
|
.stream()
|
||||||
.map(game::getPlayer)
|
.map(game::getPlayer)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(player -> game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER))
|
.map(player -> game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, true)) // must search all card parts (example: mdf commander on battlefield)
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.map(game::getPermanent)
|
.map(game::getPermanent)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,7 @@ package mage.abilities.effects;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.Duration;
|
import mage.constants.*;
|
||||||
import mage.constants.Layer;
|
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.constants.SubLayer;
|
|
||||||
import mage.filter.FilterObject;
|
import mage.filter.FilterObject;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
|
@ -62,7 +59,7 @@ public class GainAbilitySpellsEffect extends ContinuousEffectImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// workaround to gain cost reduction abilities to commanders before cast (make it playable)
|
// workaround to gain cost reduction abilities to commanders before cast (make it playable)
|
||||||
game.getCommanderCardsFromCommandZone(player).stream()
|
game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY).stream()
|
||||||
.filter(card -> filter.match(card, game))
|
.filter(card -> filter.match(card, game))
|
||||||
.forEach(card -> {
|
.forEach(card -> {
|
||||||
game.getState().addOtherAbility(card, ability);
|
game.getState().addOtherAbility(card, ability);
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,7 @@ package mage.abilities.effects.common.continuous;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.ContinuousEffectImpl;
|
import mage.abilities.effects.ContinuousEffectImpl;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.Duration;
|
import mage.constants.*;
|
||||||
import mage.constants.Layer;
|
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.constants.SubLayer;
|
|
||||||
import mage.filter.FilterCard;
|
import mage.filter.FilterCard;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
|
@ -68,7 +65,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// workaround to gain cost reduction abilities to commanders before cast (make it playable)
|
// workaround to gain cost reduction abilities to commanders before cast (make it playable)
|
||||||
game.getCommanderCardsFromCommandZone(player).stream()
|
game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY).stream()
|
||||||
.filter(card -> filter.match(card, game))
|
.filter(card -> filter.match(card, game))
|
||||||
.forEach(card -> {
|
.forEach(card -> {
|
||||||
game.getState().addOtherAbility(card, ability);
|
game.getState().addOtherAbility(card, ability);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import mage.constants.Outcome;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.events.GameEvent.EventType;
|
|
||||||
import mage.game.stack.Spell;
|
import mage.game.stack.Spell;
|
||||||
import mage.game.stack.StackObject;
|
import mage.game.stack.StackObject;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
@ -89,7 +88,7 @@ class CommanderStormEffect extends OneShotEffect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int stormCount = game
|
int stormCount = game
|
||||||
.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER)
|
.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, false)
|
||||||
.stream()
|
.stream()
|
||||||
.mapToInt(watcher::getPlaysCount)
|
.mapToInt(watcher::getPlaysCount)
|
||||||
.sum();
|
.sum();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package mage.abilities.keyword;
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.ActivatedAbilityImpl;
|
import mage.abilities.ActivatedAbilityImpl;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
|
|
@ -10,6 +9,7 @@ import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.Cards;
|
import mage.cards.Cards;
|
||||||
import mage.cards.CardsImpl;
|
import mage.cards.CardsImpl;
|
||||||
|
import mage.constants.CommanderCardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||||
|
|
@ -20,6 +20,9 @@ import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.common.TargetControlledCreaturePermanent;
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
import mage.target.common.TargetControlledPermanent;
|
import mage.target.common.TargetControlledPermanent;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 702.47. Ninjutsu
|
* 702.47. Ninjutsu
|
||||||
|
|
@ -194,16 +197,29 @@ class RevealNinjutsuCardCost extends CostImpl {
|
||||||
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||||
Player player = game.getPlayer(controllerId);
|
Player player = game.getPlayer(controllerId);
|
||||||
|
|
||||||
|
// used from hand
|
||||||
Card card = player.getHand().get(ability.getSourceId(), game);
|
Card card = player.getHand().get(ability.getSourceId(), game);
|
||||||
if (card == null && commander
|
|
||||||
&& game.getCommandersIds(player).contains(ability.getSourceId())) {
|
// rules:
|
||||||
|
// Commander ninjutsu is a variant of ninjutsu that can be activated from the command zone as
|
||||||
|
// well as from your hand. Just as with regular ninjutsu, the Ninja enters attacking the player
|
||||||
|
// or planeswalker that the returned creature was attacking.
|
||||||
|
|
||||||
|
// used from command zone
|
||||||
|
// must search all card sides for ability (example: mdf card with Ninjutsu in command zone)
|
||||||
|
if (card == null
|
||||||
|
&& commander
|
||||||
|
&& game.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, true).contains(ability.getSourceId())) {
|
||||||
for (CommandObject coj : game.getState().getCommand()) {
|
for (CommandObject coj : game.getState().getCommand()) {
|
||||||
if (coj != null && coj.getId().equals(ability.getSourceId())) {
|
if (coj != null && coj.getId().equals(ability.getSourceId())) {
|
||||||
card = game.getCard(ability.getSourceId());
|
if (CardUtil.getObjectParts(coj).contains(ability.getSourceId())) {
|
||||||
break;
|
card = game.getCard(CardUtil.getMainCardId(game, ability.getSourceId()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
Cards cards = new CardsImpl(card);
|
Cards cards = new CardsImpl(card);
|
||||||
player.revealCards("Ninjutsu", cards, game);
|
player.revealCards("Ninjutsu", cards, game);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import mage.cards.Card;
|
||||||
import mage.choices.Choice;
|
import mage.choices.Choice;
|
||||||
import mage.choices.ChoiceImpl;
|
import mage.choices.ChoiceImpl;
|
||||||
import mage.constants.ColoredManaSymbol;
|
import mage.constants.ColoredManaSymbol;
|
||||||
|
import mage.constants.CommanderCardType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.FilterMana;
|
import mage.filter.FilterMana;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
@ -71,7 +72,7 @@ class CommanderIdentityManaEffect extends ManaEffect {
|
||||||
}
|
}
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
for (UUID commanderId : game.getCommandersIds(controller)) {
|
for (UUID commanderId : game.getCommandersIds(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER, false)) {
|
||||||
Card commander = game.getCard(commanderId);
|
Card commander = game.getCard(commanderId);
|
||||||
if (commander != null) {
|
if (commander != null) {
|
||||||
FilterMana commanderMana = commander.getColorIdentity();
|
FilterMana commanderMana = commander.getColorIdentity();
|
||||||
|
|
@ -106,7 +107,7 @@ class CommanderIdentityManaEffect extends ManaEffect {
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
Choice choice = new ChoiceImpl();
|
Choice choice = new ChoiceImpl();
|
||||||
choice.setMessage("Pick a mana color");
|
choice.setMessage("Pick a mana color");
|
||||||
for (UUID commanderId : game.getCommandersIds(controller)) {
|
for (UUID commanderId : game.getCommandersIds(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER, false)) {
|
||||||
Card commander = game.getCard(commanderId);
|
Card commander = game.getCard(commanderId);
|
||||||
if (commander != null) {
|
if (commander != null) {
|
||||||
FilterMana commanderMana = commander.getColorIdentity();
|
FilterMana commanderMana = commander.getColorIdentity();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
package mage.constants;
|
package mage.constants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* rules:
|
||||||
|
* Cards that reference "your commander" instead reference "your Oathbreaker."
|
||||||
|
* <p>
|
||||||
|
* So in card rules text contains "commander" then you must use COMMANDER_OR_OATHBREAKER.
|
||||||
|
* If you card must look to command zone (e.g. target any card) then you must use ANY
|
||||||
|
*
|
||||||
* @author JayDi85
|
* @author JayDi85
|
||||||
*/
|
*/
|
||||||
public enum CommanderCardType {
|
public enum CommanderCardType {
|
||||||
|
|
|
||||||
|
|
@ -507,27 +507,44 @@ public interface Game extends MageItem, Serializable {
|
||||||
|
|
||||||
Mulligan getMulligan();
|
Mulligan getMulligan();
|
||||||
|
|
||||||
Set<UUID> getCommandersIds(Player player, CommanderCardType commanderCardType);
|
Set<UUID> getCommandersIds(Player player, CommanderCardType commanderCardType, boolean returnAllCardParts);
|
||||||
|
|
||||||
default Set<UUID> getCommandersIds(Player player) {
|
|
||||||
return getCommandersIds(player, CommanderCardType.ANY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return not played commander cards from command zone
|
* Return not played commander cards from command zone
|
||||||
|
* Read comments for CommanderCardType for more info on commanderCardType usage
|
||||||
*
|
*
|
||||||
* @param player
|
* @param player
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
default Set<Card> getCommanderCardsFromCommandZone(Player player) {
|
default Set<Card> getCommanderCardsFromCommandZone(Player player, CommanderCardType commanderCardType) {
|
||||||
// commanders in command zone aren't cards so you must call getCard instead getObject
|
// commanders in command zone aren't cards so you must call getCard instead getObject
|
||||||
return getCommandersIds(player).stream()
|
return getCommandersIds(player, commanderCardType, false).stream()
|
||||||
.map(this::getCard)
|
.map(this::getCard)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.filter(card -> Zone.COMMAND.equals(this.getState().getZone(card.getId())))
|
.filter(card -> Zone.COMMAND.equals(this.getState().getZone(card.getId())))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return commander cards from any zones (main card from command and permanent card from battlefield)
|
||||||
|
* Read comments for CommanderCardType for more info on commanderCardType usage
|
||||||
|
*
|
||||||
|
* @param player
|
||||||
|
* @param commanderCardType commander or signature spell
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
default Set<Card> getCommanderCardsFromAnyZones(Player player, CommanderCardType commanderCardType) {
|
||||||
|
// from command zone
|
||||||
|
Set<Card> res = getCommanderCardsFromCommandZone(player, commanderCardType);
|
||||||
|
|
||||||
|
// from battlefield
|
||||||
|
this.getCommandersIds(player, commanderCardType, true).stream()
|
||||||
|
.map(this::getPermanent)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(res::add);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds is it a commander card/object (use it in conditional and other things)
|
* Finds is it a commander card/object (use it in conditional and other things)
|
||||||
*
|
*
|
||||||
|
|
@ -546,7 +563,7 @@ public interface Game extends MageItem, Serializable {
|
||||||
if (object instanceof Card) {
|
if (object instanceof Card) {
|
||||||
idToCheck = ((Card) object).getMainCard().getId();
|
idToCheck = ((Card) object).getMainCard().getId();
|
||||||
}
|
}
|
||||||
return idToCheck != null && this.getCommandersIds(player).contains(idToCheck);
|
return idToCheck != null && this.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, false).contains(idToCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setGameStopped(boolean gameStopped);
|
void setGameStopped(boolean gameStopped);
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,7 @@ import mage.abilities.effects.common.continuous.CommanderReplacementEffect;
|
||||||
import mage.abilities.effects.common.cost.CommanderCostModification;
|
import mage.abilities.effects.common.cost.CommanderCostModification;
|
||||||
import mage.abilities.keyword.CompanionAbility;
|
import mage.abilities.keyword.CompanionAbility;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.MultiplayerAttackOption;
|
import mage.constants.*;
|
||||||
import mage.constants.PhaseStep;
|
|
||||||
import mage.constants.RangeOfInfluence;
|
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.game.mulligan.Mulligan;
|
import mage.game.mulligan.Mulligan;
|
||||||
import mage.game.turn.TurnMod;
|
import mage.game.turn.TurnMod;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
@ -67,7 +64,7 @@ public abstract class GameCommanderImpl extends GameImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// init commanders
|
// init commanders
|
||||||
for (UUID commanderId : this.getCommandersIds(player)) {
|
for (UUID commanderId : this.getCommandersIds(player, CommanderCardType.ANY, false)) {
|
||||||
Card commander = this.getCard(commanderId);
|
Card commander = this.getCard(commanderId);
|
||||||
if (commander != null) {
|
if (commander != null) {
|
||||||
initCommander(commander, player);
|
initCommander(commander, player);
|
||||||
|
|
@ -193,7 +190,7 @@ public abstract class GameCommanderImpl extends GameImpl {
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkStateBasedActions() {
|
protected boolean checkStateBasedActions() {
|
||||||
for (Player player : getPlayers().values()) {
|
for (Player player : getPlayers().values()) {
|
||||||
for (UUID commanderId : this.getCommandersIds(player)) {
|
for (UUID commanderId : this.getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, false)) {
|
||||||
CommanderInfoWatcher damageWatcher = getState().getWatcher(CommanderInfoWatcher.class, commanderId);
|
CommanderInfoWatcher damageWatcher = getState().getWatcher(CommanderInfoWatcher.class, commanderId);
|
||||||
if (damageWatcher == null) {
|
if (damageWatcher == null) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -1893,8 +1893,9 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
|
|
||||||
// If a commander is in a graveyard or in exile and that card was put into that zone
|
// If a commander is in a graveyard or in exile and that card was put into that zone
|
||||||
// since the last time state-based actions were checked, its owner may put it into the command zone.
|
// since the last time state-based actions were checked, its owner may put it into the command zone.
|
||||||
|
// signature spells goes to command zone all the time
|
||||||
for (Player player : state.getPlayers().values()) {
|
for (Player player : state.getPlayers().values()) {
|
||||||
Set<UUID> commanderIds = getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER);
|
Set<UUID> commanderIds = getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, false);
|
||||||
if (commanderIds.isEmpty()) {
|
if (commanderIds.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -3419,8 +3420,28 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> getCommandersIds(Player player, CommanderCardType commanderCardType) {
|
public Set<UUID> getCommandersIds(Player player, CommanderCardType commanderCardType, boolean returnAllCardParts) {
|
||||||
return player.getCommandersIds();
|
//noinspection deprecation - it's ok to use it in inner method
|
||||||
|
Set<UUID> mainCards = player.getCommandersIds();
|
||||||
|
return filterCommandersBySearchZone(mainCards, returnAllCardParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected Set<UUID> filterCommandersBySearchZone(Set<UUID> commanderMainCards, boolean returnAllCardParts) {
|
||||||
|
// filter by zone search (example: if you search commanders on battlefield then must see all sides of mdf cards)
|
||||||
|
Set<UUID> filteredCards = new HashSet<>();
|
||||||
|
if (returnAllCardParts) {
|
||||||
|
// need all card parts
|
||||||
|
commanderMainCards.stream()
|
||||||
|
.map(this::getCard)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(card -> {
|
||||||
|
filteredCards.addAll(CardUtil.getObjectParts(card));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
filteredCards.addAll(commanderMainCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredCards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ import mage.filter.predicate.permanent.PermanentIdPredicate;
|
||||||
import mage.game.*;
|
import mage.game.*;
|
||||||
import mage.game.combat.CombatGroup;
|
import mage.game.combat.CombatGroup;
|
||||||
import mage.game.command.CommandObject;
|
import mage.game.command.CommandObject;
|
||||||
import mage.game.command.Commander;
|
|
||||||
import mage.game.events.*;
|
import mage.game.events.*;
|
||||||
import mage.game.match.MatchPlayer;
|
import mage.game.match.MatchPlayer;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
|
@ -314,7 +313,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
this.sideboard = player.getSideboard().copy();
|
this.sideboard = player.getSideboard().copy();
|
||||||
this.hand = player.getHand().copy();
|
this.hand = player.getHand().copy();
|
||||||
this.graveyard = player.getGraveyard().copy();
|
this.graveyard = player.getGraveyard().copy();
|
||||||
|
|
||||||
|
//noinspection deprecation - it's ok to use it in inner methods
|
||||||
this.commandersIds = new HashSet<>(player.getCommandersIds());
|
this.commandersIds = new HashSet<>(player.getCommandersIds());
|
||||||
|
|
||||||
this.abilities = player.getAbilities().copy();
|
this.abilities = player.getAbilities().copy();
|
||||||
this.counters = player.getCounters().copy();
|
this.counters = player.getCounters().copy();
|
||||||
|
|
||||||
|
|
@ -1578,7 +1580,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
try {
|
try {
|
||||||
// collect and filter playable activated abilities
|
// collect and filter playable activated abilities
|
||||||
// GUI: user clicks on card, but it must activate ability from ANY card's parts (main, left, right)
|
// GUI: user clicks on card, but it must activate ability from ANY card's parts (main, left, right)
|
||||||
Set<UUID> needIds = getObjectParts(object);
|
Set<UUID> needIds = CardUtil.getObjectParts(object);
|
||||||
|
|
||||||
// workaround to find all abilities first and filter it for one object
|
// workaround to find all abilities first and filter it for one object
|
||||||
List<ActivatedAbility> allPlayable = getPlayable(game, true, zone, false);
|
List<ActivatedAbility> allPlayable = getPlayable(game, true, zone, false);
|
||||||
|
|
@ -1593,40 +1595,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
return useable;
|
return useable;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Set<UUID> getObjectParts(MageObject object) {
|
|
||||||
// collect all possible object's parts (example: all sides in mdf/split cards)
|
|
||||||
Set<UUID> res = new HashSet<>();
|
|
||||||
if (object instanceof SplitCard || object instanceof SplitCardHalf) {
|
|
||||||
SplitCard mainCard = (SplitCard) ((Card) object).getMainCard();
|
|
||||||
res.add(object.getId());
|
|
||||||
res.add(mainCard.getId());
|
|
||||||
res.add(mainCard.getLeftHalfCard().getId());
|
|
||||||
res.add(mainCard.getRightHalfCard().getId());
|
|
||||||
} else if (object instanceof ModalDoubleFacesCard || object instanceof ModalDoubleFacesCardHalf) {
|
|
||||||
ModalDoubleFacesCard mainCard = (ModalDoubleFacesCard) ((Card) object).getMainCard();
|
|
||||||
res.add(object.getId());
|
|
||||||
res.add(mainCard.getId());
|
|
||||||
res.add(mainCard.getLeftHalfCard().getId());
|
|
||||||
res.add(mainCard.getRightHalfCard().getId());
|
|
||||||
} else if (object instanceof AdventureCard || object instanceof AdventureCardSpell) {
|
|
||||||
AdventureCard mainCard = (AdventureCard) ((Card) object).getMainCard();
|
|
||||||
res.add(object.getId());
|
|
||||||
res.add(mainCard.getId());
|
|
||||||
res.add(mainCard.getSpellCard().getId());
|
|
||||||
} else if (object instanceof Spell) {
|
|
||||||
// example: activate Lightning Storm's ability from the spell on the stack
|
|
||||||
res.add(object.getId());
|
|
||||||
res.add(((Spell) object).getCard().getId()); // only single side goes to the stack
|
|
||||||
} else if (object instanceof Commander) {
|
|
||||||
// commander can contains double sides
|
|
||||||
res.add(object.getId());
|
|
||||||
res.addAll(getObjectParts(((Commander) object).getSourceObject()));
|
|
||||||
} else {
|
|
||||||
res.add(object.getId());
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected LinkedHashMap<UUID, ActivatedManaAbilityImpl> getUseableManaAbilities(MageObject object, Zone zone, Game game) {
|
protected LinkedHashMap<UUID, ActivatedManaAbilityImpl> getUseableManaAbilities(MageObject object, Zone zone, Game game) {
|
||||||
LinkedHashMap<UUID, ActivatedManaAbilityImpl> useable = new LinkedHashMap<>();
|
LinkedHashMap<UUID, ActivatedManaAbilityImpl> useable = new LinkedHashMap<>();
|
||||||
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import mage.MageItem;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.Cards;
|
import mage.cards.Cards;
|
||||||
|
import mage.constants.CommanderCardType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.FilterCard;
|
import mage.filter.FilterCard;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
|
||||||
import mage.game.events.TargetEvent;
|
import mage.game.events.TargetEvent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
|
|
@ -115,6 +115,22 @@ public class TargetCard extends TargetObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case COMMAND:
|
||||||
|
List<Card> possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream()
|
||||||
|
.map(game::getCard)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND))
|
||||||
|
.filter(card -> filter.match(card, sourceId, sourceControllerId, game))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (Card card : possibleCards) {
|
||||||
|
if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) {
|
||||||
|
possibleTargets++;
|
||||||
|
if (possibleTargets >= this.minNumberOfTargets) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,7 +189,7 @@ public class TargetCard extends TargetObject {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case COMMAND:
|
case COMMAND:
|
||||||
List<Card> possibleCards = game.getCommandersIds(player).stream()
|
List<Card> possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream()
|
||||||
.map(game::getCard)
|
.map(game::getCard)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND))
|
.filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND))
|
||||||
|
|
|
||||||
|
|
@ -1177,4 +1177,50 @@ public final class CardUtil {
|
||||||
public static boolean checkCostWords(String text) {
|
public static boolean checkCostWords(String text) {
|
||||||
return text != null && costWords.stream().anyMatch(text.toLowerCase(Locale.ENGLISH)::startsWith);
|
return text != null && costWords.stream().anyMatch(text.toLowerCase(Locale.ENGLISH)::startsWith);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect all possible object's parts (example: all sides in mdf/split cards)
|
||||||
|
* <p>
|
||||||
|
* Works with any objects, so commander object can return four ids: commander + main card + left card + right card
|
||||||
|
* If you pass Card object then it return main card + all parts
|
||||||
|
*
|
||||||
|
* @param object
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Set<UUID> getObjectParts(MageObject object) {
|
||||||
|
Set<UUID> res = new HashSet<>();
|
||||||
|
if (object == null) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object instanceof SplitCard || object instanceof SplitCardHalf) {
|
||||||
|
SplitCard mainCard = (SplitCard) ((Card) object).getMainCard();
|
||||||
|
res.add(object.getId());
|
||||||
|
res.add(mainCard.getId());
|
||||||
|
res.add(mainCard.getLeftHalfCard().getId());
|
||||||
|
res.add(mainCard.getRightHalfCard().getId());
|
||||||
|
} else if (object instanceof ModalDoubleFacesCard || object instanceof ModalDoubleFacesCardHalf) {
|
||||||
|
ModalDoubleFacesCard mainCard = (ModalDoubleFacesCard) ((Card) object).getMainCard();
|
||||||
|
res.add(object.getId());
|
||||||
|
res.add(mainCard.getId());
|
||||||
|
res.add(mainCard.getLeftHalfCard().getId());
|
||||||
|
res.add(mainCard.getRightHalfCard().getId());
|
||||||
|
} else if (object instanceof AdventureCard || object instanceof AdventureCardSpell) {
|
||||||
|
AdventureCard mainCard = (AdventureCard) ((Card) object).getMainCard();
|
||||||
|
res.add(object.getId());
|
||||||
|
res.add(mainCard.getId());
|
||||||
|
res.add(mainCard.getSpellCard().getId());
|
||||||
|
} else if (object instanceof Spell) {
|
||||||
|
// example: activate Lightning Storm's ability from the spell on the stack
|
||||||
|
res.add(object.getId());
|
||||||
|
res.addAll(getObjectParts(((Spell) object).getCard()));
|
||||||
|
} else if (object instanceof Commander) {
|
||||||
|
// commander can contains double sides
|
||||||
|
res.add(object.getId());
|
||||||
|
res.addAll(getObjectParts(((Commander) object).getSourceObject()));
|
||||||
|
} else {
|
||||||
|
res.add(object.getId());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package mage.watchers.common;
|
package mage.watchers.common;
|
||||||
|
|
||||||
|
import mage.constants.CommanderCardType;
|
||||||
import mage.constants.WatcherScope;
|
import mage.constants.WatcherScope;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
@ -45,11 +46,12 @@ public class CommanderPlaysCountWatcher extends Watcher {
|
||||||
objectId = null;
|
objectId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// must calc all commanders and signature spell cause uses in commander tax
|
||||||
boolean isCommanderObject = game
|
boolean isCommanderObject = game
|
||||||
.getPlayerList()
|
.getPlayerList()
|
||||||
.stream()
|
.stream()
|
||||||
.map(game::getPlayer)
|
.map(game::getPlayer)
|
||||||
.map(game::getCommandersIds)
|
.map(player -> game.getCommandersIds(player, CommanderCardType.ANY, false))
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.anyMatch(id -> Objects.equals(id, objectId));
|
.anyMatch(id -> Objects.equals(id, objectId));
|
||||||
if (!isCommanderObject || event.getZone() != Zone.COMMAND) {
|
if (!isCommanderObject || event.getZone() != Zone.COMMAND) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue