forked from External/mage
[LCI] Implement Fabrication Foundry; Unstable Glyphbridge (#11521)
* First pass at Fabrication Foundry * misc cleanup * First pass at Unstable Glyphbridge/Sandswirl Wanderglyph * Set up backside correctly * Fix Unstable Bridge cast condition * Improve Fabrication Foundry to use Crew-like hint * Fix equality check Tested, though only manually. Alongside the direct card implementations, I also: - Fix Oaken Siren's mana to not be able to pay for soft counterspells from artifact sources - Change PlayersAttackedThisTurnWatcher to follow the rules better and to have a separate section for planeswalkers' controllers who were attacked (needed for Sandswirl Wanderglyph) - Removed an unused constructor in ExileTargetCost (though I didn't end up using that cost in the end) - minor cleanup in CrewAbility
This commit is contained in:
parent
3bc28d63c3
commit
ec0166bf7f
8 changed files with 449 additions and 14 deletions
179
Mage.Sets/src/mage/cards/f/FabricationFoundry.java
Normal file
179
Mage.Sets/src/mage/cards/f/FabricationFoundry.java
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
package mage.cards.f;
|
||||||
|
|
||||||
|
import mage.ConditionalMana;
|
||||||
|
import mage.MageObject;
|
||||||
|
import mage.Mana;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.ActivateAsSorceryActivatedAbility;
|
||||||
|
import mage.abilities.condition.Condition;
|
||||||
|
import mage.abilities.costs.Cost;
|
||||||
|
import mage.abilities.costs.CostImpl;
|
||||||
|
import mage.abilities.costs.common.TapSourceCost;
|
||||||
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
|
||||||
|
import mage.abilities.hint.HintUtils;
|
||||||
|
import mage.abilities.mana.ConditionalColoredManaAbility;
|
||||||
|
import mage.abilities.mana.builder.ConditionalManaBuilder;
|
||||||
|
import mage.cards.*;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.filter.common.FilterControlledArtifactPermanent;
|
||||||
|
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.Target;
|
||||||
|
import mage.target.TargetPermanent;
|
||||||
|
import mage.target.common.TargetCardInYourGraveyard;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author notgreat
|
||||||
|
*/
|
||||||
|
public final class FabricationFoundry extends CardImpl {
|
||||||
|
|
||||||
|
public FabricationFoundry(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}");
|
||||||
|
|
||||||
|
|
||||||
|
// {T}: Add {W}. Spend this mana only to cast an artifact spell or activate an ability of an artifact source.
|
||||||
|
this.addAbility(new ConditionalColoredManaAbility(Mana.WhiteMana(1), new ArtifactManaBuilder()));
|
||||||
|
|
||||||
|
// {2}{W}, {T}, Exile one or more other artifacts you control with total mana value X: Return target artifact card with mana value X or less from your graveyard to the battlefield. Activate only as a sorcery.
|
||||||
|
Ability ability = new ActivateAsSorceryActivatedAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl<>("{2}{W}"));
|
||||||
|
ability.addCost(new TapSourceCost());
|
||||||
|
ability.addCost(new ExileTargetsTotalManaValueCost());
|
||||||
|
Target target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_ARTIFACT_FROM_YOUR_GRAVEYARD);
|
||||||
|
target.setTargetName("artifact card with mana value X or less from your graveyard");
|
||||||
|
ability.addTarget(target);
|
||||||
|
this.addAbility(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FabricationFoundry(final FabricationFoundry card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FabricationFoundry copy() {
|
||||||
|
return new FabricationFoundry(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Mana based on Oaken Siren
|
||||||
|
class ArtifactManaBuilder extends ConditionalManaBuilder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConditionalMana build(Object... options) {
|
||||||
|
return new ArtifactConditionalMana(this.mana);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRule() {
|
||||||
|
return "Spend this mana only to cast an artifact spell or activate an ability of an artifact source";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArtifactConditionalMana extends ConditionalMana {
|
||||||
|
|
||||||
|
ArtifactConditionalMana(Mana mana) {
|
||||||
|
super(mana);
|
||||||
|
addCondition(ArtifactSpellOrActivatedAbilityCondition.instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ArtifactSpellOrActivatedAbilityCondition implements Condition {
|
||||||
|
instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
MageObject object = game.getObject(source);
|
||||||
|
return object != null && object.isArtifact(game) && !source.isActivated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Cost based on Kozilek, The Great Distortion and CrewAbility
|
||||||
|
class ExileTargetsTotalManaValueCost extends CostImpl {
|
||||||
|
private static final FilterPermanent filter = new FilterControlledArtifactPermanent("one or more other artifacts you control with total mana value X");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(AnotherPredicate.instance);
|
||||||
|
}
|
||||||
|
public ExileTargetsTotalManaValueCost() {
|
||||||
|
this.text = "Exile one or more other artifacts you control with total mana value X";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExileTargetsTotalManaValueCost(ExileTargetsTotalManaValueCost cost) {
|
||||||
|
super(cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||||
|
Card abilityTarget = game.getCard(ability.getFirstTarget());
|
||||||
|
if (abilityTarget == null) {
|
||||||
|
return paid;
|
||||||
|
}
|
||||||
|
Player player = game.getPlayer(ability.getControllerId());
|
||||||
|
if (player == null) {
|
||||||
|
return paid;
|
||||||
|
}
|
||||||
|
int minX = abilityTarget.getManaValue();
|
||||||
|
int sum = 0;
|
||||||
|
Target target = new TargetPermanent(1, Integer.MAX_VALUE, filter, true){
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
// shows selected mana value
|
||||||
|
int selectedPower = this.targets.keySet().stream()
|
||||||
|
.map(game::getPermanent)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.mapToInt(Permanent::getManaValue)
|
||||||
|
.sum();
|
||||||
|
String extraInfo = "(selected mana value " + selectedPower + " of " + minX + ")";
|
||||||
|
if (selectedPower >= minX) {
|
||||||
|
extraInfo = HintUtils.prepareText(extraInfo, Color.GREEN);
|
||||||
|
}
|
||||||
|
return super.getMessage() + " " + extraInfo;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!target.choose(Outcome.Exile, controllerId, source.getSourceId(), source, game)){
|
||||||
|
return paid;
|
||||||
|
}
|
||||||
|
Cards cards = new CardsImpl();
|
||||||
|
cards.addAll(target.getTargets());
|
||||||
|
for (UUID targetId : target.getTargets()) {
|
||||||
|
Permanent permanent = game.getPermanent(targetId);
|
||||||
|
if (permanent != null) {
|
||||||
|
sum += permanent.getManaValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paid = (sum >= minX);
|
||||||
|
if (paid) {
|
||||||
|
player.moveCardsToExile(cards.getCards(game), source, game, false,null,null);
|
||||||
|
}
|
||||||
|
return paid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
|
int totalExileMV = 0;
|
||||||
|
boolean anyExileFound = false;
|
||||||
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, controllerId, source, game)){
|
||||||
|
totalExileMV += permanent.getManaValue();
|
||||||
|
anyExileFound = true;
|
||||||
|
}
|
||||||
|
int minTargetMV = Integer.MAX_VALUE;
|
||||||
|
for (Card card : game.getPlayer(controllerId).getGraveyard().getCards(StaticFilters.FILTER_CARD_ARTIFACT_FROM_YOUR_GRAVEYARD, game)){
|
||||||
|
minTargetMV = Integer.min(minTargetMV, card.getManaValue());
|
||||||
|
}
|
||||||
|
return anyExileFound && totalExileMV >= minTargetMV;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExileTargetsTotalManaValueCost copy() {
|
||||||
|
return new ExileTargetsTotalManaValueCost(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -78,6 +78,6 @@ enum OakenSirenCondition implements Condition {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
MageObject object = game.getObject(source);
|
MageObject object = game.getObject(source);
|
||||||
return object != null && object.isArtifact(game);
|
return object != null && object.isArtifact(game) && !source.isActivated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
123
Mage.Sets/src/mage/cards/s/SandswirlWanderglyph.java
Normal file
123
Mage.Sets/src/mage/cards/s/SandswirlWanderglyph.java
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
package mage.cards.s;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.common.SpellCastOpponentTriggeredAbility;
|
||||||
|
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||||
|
import mage.abilities.effects.RestrictionEffect;
|
||||||
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.filter.FilterSpell;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.watchers.common.PlayersAttackedThisTurnWatcher;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author notgreat
|
||||||
|
*/
|
||||||
|
public final class SandswirlWanderglyph extends CardImpl {
|
||||||
|
private static final FilterSpell filter = new FilterSpell("a spell during their turn");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(TargetController.ACTIVE.getControllerPredicate());
|
||||||
|
}
|
||||||
|
public SandswirlWanderglyph(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.GOLEM);
|
||||||
|
this.power = new MageInt(5);
|
||||||
|
this.toughness = new MageInt(3);
|
||||||
|
this.nightCard = true;
|
||||||
|
this.color.setWhite(true);
|
||||||
|
|
||||||
|
// Flying
|
||||||
|
this.addAbility(FlyingAbility.getInstance());
|
||||||
|
|
||||||
|
// Whenever an opponent casts a spell during their turn, they can't attack you or planeswalkers you control this turn.
|
||||||
|
this.addAbility(new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD,
|
||||||
|
new CantAttackSourcePlayerOrPlaneswalkerThisTurnEffect(), filter, false, SetTargetPointer.PLAYER));
|
||||||
|
|
||||||
|
// Each opponent who attacked you or a planeswalker you control this turn can't cast spells.
|
||||||
|
this.addAbility(new SimpleStaticAbility(new SandswirlWanderglyphCantCastEffect()), new PlayersAttackedThisTurnWatcher());
|
||||||
|
}
|
||||||
|
|
||||||
|
private SandswirlWanderglyph(final SandswirlWanderglyph card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SandswirlWanderglyph copy() {
|
||||||
|
return new SandswirlWanderglyph(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SandswirlWanderglyphCantCastEffect extends ContinuousRuleModifyingEffectImpl {
|
||||||
|
|
||||||
|
public SandswirlWanderglyphCantCastEffect() {
|
||||||
|
super(Duration.WhileOnBattlefield, Outcome.Benefit);
|
||||||
|
staticText = "Each opponent who attacked you or a planeswalker you control this turn can't cast spells";
|
||||||
|
}
|
||||||
|
|
||||||
|
private SandswirlWanderglyphCantCastEffect(final SandswirlWanderglyphCantCastEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SandswirlWanderglyphCantCastEffect copy() {
|
||||||
|
return new SandswirlWanderglyphCantCastEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checksEventType(GameEvent event, Game game) {
|
||||||
|
return event.getType() == GameEvent.EventType.CAST_SPELL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||||
|
if (game.isActivePlayer(event.getPlayerId()) && game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) {
|
||||||
|
PlayersAttackedThisTurnWatcher watcher = game.getState().getWatcher(PlayersAttackedThisTurnWatcher.class);
|
||||||
|
return watcher != null && watcher.hasPlayerAttackedPlayerOrControlledPlaneswalker(event.getPlayerId(), source.getControllerId());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
class CantAttackSourcePlayerOrPlaneswalkerThisTurnEffect extends RestrictionEffect {
|
||||||
|
CantAttackSourcePlayerOrPlaneswalkerThisTurnEffect() {
|
||||||
|
super(Duration.EndOfTurn);
|
||||||
|
staticText = "they can't attack you or planeswalkers you control this turn";
|
||||||
|
}
|
||||||
|
|
||||||
|
private CantAttackSourcePlayerOrPlaneswalkerThisTurnEffect(final CantAttackSourcePlayerOrPlaneswalkerThisTurnEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CantAttackSourcePlayerOrPlaneswalkerThisTurnEffect copy() {
|
||||||
|
return new CantAttackSourcePlayerOrPlaneswalkerThisTurnEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) {
|
||||||
|
if (game.getPlayer(defenderId) != null){
|
||||||
|
return !(source.getControllerId().equals(defenderId));
|
||||||
|
}
|
||||||
|
Permanent defender = game.getPermanent(defenderId);
|
||||||
|
if (defender != null && defender.isPlaneswalker()){
|
||||||
|
return !(source.getControllerId().equals(defender.getControllerId()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applies(Permanent permanent, Ability source, Game game) {
|
||||||
|
return permanent.getControllerId().equals(getTargetPointer().getFirst(game, source));
|
||||||
|
}
|
||||||
|
}
|
||||||
105
Mage.Sets/src/mage/cards/u/UnstableGlyphbridge.java
Normal file
105
Mage.Sets/src/mage/cards/u/UnstableGlyphbridge.java
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
package mage.cards.u;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
|
import mage.abilities.condition.common.CastFromEverywhereSourceCondition;
|
||||||
|
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.keyword.CraftAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.cards.Cards;
|
||||||
|
import mage.cards.CardsImpl;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.ComparisonType;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
|
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||||
|
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author notgreat
|
||||||
|
*/
|
||||||
|
public final class UnstableGlyphbridge extends CardImpl {
|
||||||
|
|
||||||
|
public UnstableGlyphbridge(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}{W}{W}");
|
||||||
|
this.secondSideCardClazz = mage.cards.s.SandswirlWanderglyph.class;
|
||||||
|
|
||||||
|
// When Unstable Glyphbridge enters the battlefield, if you cast it, for each player, choose a creature with power 2 or less that player controls. Then destroy all creatures except creatures chosen this way.
|
||||||
|
this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(
|
||||||
|
new UnstableGlyphbridgeEffect()), CastFromEverywhereSourceCondition.instance,
|
||||||
|
"When {this} enters the battlefield, if you cast it, " +
|
||||||
|
"for each player, choose a creature with power 2 or less that player controls. " +
|
||||||
|
"Then destroy all creatures except creatures chosen this way."
|
||||||
|
));
|
||||||
|
|
||||||
|
// Craft with artifact {3}{W}{W}
|
||||||
|
this.addAbility(new CraftAbility("{3}{W}{W}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private UnstableGlyphbridge(final UnstableGlyphbridge card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnstableGlyphbridge copy() {
|
||||||
|
return new UnstableGlyphbridge(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class UnstableGlyphbridgeEffect extends OneShotEffect {
|
||||||
|
UnstableGlyphbridgeEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "for each player, choose a creature with power 2 or less that player controls. " +
|
||||||
|
"Then destroy all creatures except creatures chosen this way.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private UnstableGlyphbridgeEffect(final UnstableGlyphbridgeEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnstableGlyphbridgeEffect copy() {
|
||||||
|
return new UnstableGlyphbridgeEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
|
if (controller == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Cards cards = new CardsImpl();
|
||||||
|
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
|
||||||
|
Player player = game.getPlayer(playerId);
|
||||||
|
if (player == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with power 2 or less");
|
||||||
|
filter.add(new PowerPredicate(ComparisonType.OR_LESS,2));
|
||||||
|
filter.add(new ControllerIdPredicate(playerId));
|
||||||
|
TargetCreaturePermanent target = new TargetCreaturePermanent(filter);
|
||||||
|
target.withNotTarget(true);
|
||||||
|
target.withChooseHint(player.getName() + " controls");
|
||||||
|
|
||||||
|
controller.choose(Outcome.PutCreatureInPlay, target, source, game);
|
||||||
|
cards.add(target.getFirstTarget());
|
||||||
|
}
|
||||||
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES, source.getControllerId(), source, game)) {
|
||||||
|
if (!cards.contains(permanent.getId())) {
|
||||||
|
permanent.destroy(source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -134,6 +134,7 @@ public final class TheLostCavernsOfIxalan extends ExpansionSet {
|
||||||
cards.add(new SetCardInfo("Envoy of Okinec Ahau", 11, Rarity.COMMON, mage.cards.e.EnvoyOfOkinecAhau.class));
|
cards.add(new SetCardInfo("Envoy of Okinec Ahau", 11, Rarity.COMMON, mage.cards.e.EnvoyOfOkinecAhau.class));
|
||||||
cards.add(new SetCardInfo("Etali's Favor", 149, Rarity.COMMON, mage.cards.e.EtalisFavor.class));
|
cards.add(new SetCardInfo("Etali's Favor", 149, Rarity.COMMON, mage.cards.e.EtalisFavor.class));
|
||||||
cards.add(new SetCardInfo("Explorer's Cache", 184, Rarity.UNCOMMON, mage.cards.e.ExplorersCache.class));
|
cards.add(new SetCardInfo("Explorer's Cache", 184, Rarity.UNCOMMON, mage.cards.e.ExplorersCache.class));
|
||||||
|
cards.add(new SetCardInfo("Fabrication Foundry", 12, Rarity.RARE, mage.cards.f.FabricationFoundry.class));
|
||||||
cards.add(new SetCardInfo("Family Reunion", 13, Rarity.COMMON, mage.cards.f.FamilyReunion.class));
|
cards.add(new SetCardInfo("Family Reunion", 13, Rarity.COMMON, mage.cards.f.FamilyReunion.class));
|
||||||
cards.add(new SetCardInfo("Fanatical Offering", 105, Rarity.COMMON, mage.cards.f.FanaticalOffering.class));
|
cards.add(new SetCardInfo("Fanatical Offering", 105, Rarity.COMMON, mage.cards.f.FanaticalOffering.class));
|
||||||
cards.add(new SetCardInfo("Forest", 291, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_UST_VARIOUS));
|
cards.add(new SetCardInfo("Forest", 291, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_UST_VARIOUS));
|
||||||
|
|
@ -273,6 +274,7 @@ public final class TheLostCavernsOfIxalan extends ExpansionSet {
|
||||||
cards.add(new SetCardInfo("Sage of Days", 73, Rarity.COMMON, mage.cards.s.SageOfDays.class));
|
cards.add(new SetCardInfo("Sage of Days", 73, Rarity.COMMON, mage.cards.s.SageOfDays.class));
|
||||||
cards.add(new SetCardInfo("Saheeli's Lattice", 164, Rarity.UNCOMMON, mage.cards.s.SaheelisLattice.class));
|
cards.add(new SetCardInfo("Saheeli's Lattice", 164, Rarity.UNCOMMON, mage.cards.s.SaheelisLattice.class));
|
||||||
cards.add(new SetCardInfo("Saheeli, the Sun's Brilliance", 239, Rarity.MYTHIC, mage.cards.s.SaheeliTheSunsBrilliance.class));
|
cards.add(new SetCardInfo("Saheeli, the Sun's Brilliance", 239, Rarity.MYTHIC, mage.cards.s.SaheeliTheSunsBrilliance.class));
|
||||||
|
cards.add(new SetCardInfo("Sandswirl Wanderglyph", 41, Rarity.RARE, mage.cards.s.SandswirlWanderglyph.class));
|
||||||
cards.add(new SetCardInfo("Sanguine Evangelist", 34, Rarity.RARE, mage.cards.s.SanguineEvangelist.class));
|
cards.add(new SetCardInfo("Sanguine Evangelist", 34, Rarity.RARE, mage.cards.s.SanguineEvangelist.class));
|
||||||
cards.add(new SetCardInfo("Scampering Surveyor", 260, Rarity.UNCOMMON, mage.cards.s.ScamperingSurveyor.class));
|
cards.add(new SetCardInfo("Scampering Surveyor", 260, Rarity.UNCOMMON, mage.cards.s.ScamperingSurveyor.class));
|
||||||
cards.add(new SetCardInfo("Screaming Phantom", 118, Rarity.COMMON, mage.cards.s.ScreamingPhantom.class));
|
cards.add(new SetCardInfo("Screaming Phantom", 118, Rarity.COMMON, mage.cards.s.ScreamingPhantom.class));
|
||||||
|
|
@ -347,6 +349,7 @@ public final class TheLostCavernsOfIxalan extends ExpansionSet {
|
||||||
cards.add(new SetCardInfo("Twists and Turns", 217, Rarity.UNCOMMON, mage.cards.t.TwistsAndTurns.class));
|
cards.add(new SetCardInfo("Twists and Turns", 217, Rarity.UNCOMMON, mage.cards.t.TwistsAndTurns.class));
|
||||||
cards.add(new SetCardInfo("Uchbenbak, the Great Mistake", 242, Rarity.UNCOMMON, mage.cards.u.UchbenbakTheGreatMistake.class));
|
cards.add(new SetCardInfo("Uchbenbak, the Great Mistake", 242, Rarity.UNCOMMON, mage.cards.u.UchbenbakTheGreatMistake.class));
|
||||||
cards.add(new SetCardInfo("Unlucky Drop", 82, Rarity.COMMON, mage.cards.u.UnluckyDrop.class));
|
cards.add(new SetCardInfo("Unlucky Drop", 82, Rarity.COMMON, mage.cards.u.UnluckyDrop.class));
|
||||||
|
cards.add(new SetCardInfo("Unstable Glyphbridge", 41, Rarity.RARE, mage.cards.u.UnstableGlyphbridge.class));
|
||||||
cards.add(new SetCardInfo("Vanguard of the Rose", 42, Rarity.UNCOMMON, mage.cards.v.VanguardOfTheRose.class));
|
cards.add(new SetCardInfo("Vanguard of the Rose", 42, Rarity.UNCOMMON, mage.cards.v.VanguardOfTheRose.class));
|
||||||
cards.add(new SetCardInfo("Visage of Dread", 129, Rarity.UNCOMMON, mage.cards.v.VisageOfDread.class));
|
cards.add(new SetCardInfo("Visage of Dread", 129, Rarity.UNCOMMON, mage.cards.v.VisageOfDread.class));
|
||||||
cards.add(new SetCardInfo("Vito's Inquisitor", 130, Rarity.COMMON, mage.cards.v.VitosInquisitor.class));
|
cards.add(new SetCardInfo("Vito's Inquisitor", 130, Rarity.COMMON, mage.cards.v.VitosInquisitor.class));
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,6 @@ public class ExileTargetCost extends CostImpl {
|
||||||
this.text = "exile " + target.getDescription();
|
this.text = "exile " + target.getDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExileTargetCost(TargetControlledPermanent target, boolean noText) {
|
|
||||||
this.addTarget(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExileTargetCost(ExileTargetCost cost) {
|
public ExileTargetCost(ExileTargetCost cost) {
|
||||||
super(cost);
|
super(cost);
|
||||||
for (Permanent permanent : cost.permanents) {
|
for (Permanent permanent : cost.permanents) {
|
||||||
|
|
|
||||||
|
|
@ -152,8 +152,8 @@ class CrewCost extends CostImpl {
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
// shows selected power
|
// shows selected power
|
||||||
int selectedPower = this.targets.entrySet().stream()
|
int selectedPower = this.targets.keySet().stream()
|
||||||
.map(entry -> (game.getPermanent(entry.getKey())))
|
.map(game::getPermanent)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.mapToInt(p -> (getCrewPower(p, game)))
|
.mapToInt(p -> (getCrewPower(p, game)))
|
||||||
.sum();
|
.sum();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package mage.watchers.common;
|
||||||
import mage.constants.WatcherScope;
|
import mage.constants.WatcherScope;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.PlayerList;
|
import mage.players.PlayerList;
|
||||||
import mage.watchers.Watcher;
|
import mage.watchers.Watcher;
|
||||||
|
|
||||||
|
|
@ -18,6 +19,7 @@ public class PlayersAttackedThisTurnWatcher extends Watcher {
|
||||||
// how many players or opponents each player attacked this turn
|
// how many players or opponents each player attacked this turn
|
||||||
private final Map<UUID, PlayerList> playersAttackedThisTurn = new HashMap<>();
|
private final Map<UUID, PlayerList> playersAttackedThisTurn = new HashMap<>();
|
||||||
private final Map<UUID, PlayerList> opponentsAttackedThisTurn = new HashMap<>();
|
private final Map<UUID, PlayerList> opponentsAttackedThisTurn = new HashMap<>();
|
||||||
|
private final Map<UUID, PlayerList> planeswalkerControllerAttackedThisTurn = new HashMap<>();
|
||||||
|
|
||||||
public PlayersAttackedThisTurnWatcher() {
|
public PlayersAttackedThisTurnWatcher() {
|
||||||
super(WatcherScope.GAME);
|
super(WatcherScope.GAME);
|
||||||
|
|
@ -25,10 +27,10 @@ public class PlayersAttackedThisTurnWatcher extends Watcher {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void watch(GameEvent event, Game game) {
|
public void watch(GameEvent event, Game game) {
|
||||||
if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) {
|
/*
|
||||||
playersAttackedThisTurn.clear();
|
Faramir, Prince of Ithilien:
|
||||||
opponentsAttackedThisTurn.clear();
|
Attacking a planeswalker you control or battle you're protecting doesn't count as attacking you. (2023-06-16)
|
||||||
}
|
*/
|
||||||
|
|
||||||
if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) {
|
if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) {
|
||||||
|
|
||||||
|
|
@ -37,8 +39,8 @@ public class PlayersAttackedThisTurnWatcher extends Watcher {
|
||||||
if (playersAttacked == null) {
|
if (playersAttacked == null) {
|
||||||
playersAttacked = new PlayerList();
|
playersAttacked = new PlayerList();
|
||||||
}
|
}
|
||||||
UUID playerDefender = game.getCombat().getDefendingPlayerId(event.getSourceId(), game);
|
UUID playerDefender = event.getTargetId();
|
||||||
if (playerDefender != null
|
if (playerDefender != null && game.getPlayer(playerDefender) != null
|
||||||
&& !playersAttacked.contains(playerDefender)) {
|
&& !playersAttacked.contains(playerDefender)) {
|
||||||
playersAttacked.add(playerDefender);
|
playersAttacked.add(playerDefender);
|
||||||
}
|
}
|
||||||
|
|
@ -49,13 +51,26 @@ public class PlayersAttackedThisTurnWatcher extends Watcher {
|
||||||
if (opponentsAttacked == null) {
|
if (opponentsAttacked == null) {
|
||||||
opponentsAttacked = new PlayerList();
|
opponentsAttacked = new PlayerList();
|
||||||
}
|
}
|
||||||
UUID opponentDefender = game.getCombat().getDefendingPlayerId(event.getSourceId(), game);
|
UUID opponentDefender = event.getTargetId();
|
||||||
if (opponentDefender != null
|
if (opponentDefender != null
|
||||||
&& game.getOpponents(event.getPlayerId()).contains(opponentDefender)
|
&& game.getOpponents(event.getPlayerId()).contains(opponentDefender)
|
||||||
&& !opponentsAttacked.contains(opponentDefender)) {
|
&& !opponentsAttacked.contains(opponentDefender)) {
|
||||||
opponentsAttacked.add(opponentDefender);
|
opponentsAttacked.add(opponentDefender);
|
||||||
}
|
}
|
||||||
opponentsAttackedThisTurn.putIfAbsent(event.getPlayerId(), opponentsAttacked);
|
opponentsAttackedThisTurn.putIfAbsent(event.getPlayerId(), opponentsAttacked);
|
||||||
|
|
||||||
|
//Planeswalker controllers
|
||||||
|
PlayerList controllersAttacked = planeswalkerControllerAttackedThisTurn.get(event.getPlayerId());
|
||||||
|
if (controllersAttacked == null) {
|
||||||
|
controllersAttacked = new PlayerList();
|
||||||
|
}
|
||||||
|
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||||
|
UUID controllingDefender = game.getCombat().getDefendingPlayerId(event.getSourceId(), game);
|
||||||
|
if (controllingDefender != null && permanent != null && permanent.isPlaneswalker(game)
|
||||||
|
&& !controllersAttacked.contains(controllingDefender)) {
|
||||||
|
controllersAttacked.add(controllingDefender);
|
||||||
|
}
|
||||||
|
planeswalkerControllerAttackedThisTurn.putIfAbsent(event.getPlayerId(), controllersAttacked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,6 +78,12 @@ public class PlayersAttackedThisTurnWatcher extends Watcher {
|
||||||
PlayerList defendersList = playersAttackedThisTurn.getOrDefault(attacker, null);
|
PlayerList defendersList = playersAttackedThisTurn.getOrDefault(attacker, null);
|
||||||
return defendersList != null && defendersList.contains(defender);
|
return defendersList != null && defendersList.contains(defender);
|
||||||
}
|
}
|
||||||
|
public boolean hasPlayerAttackedPlayerOrControlledPlaneswalker(UUID attacker, UUID defender){
|
||||||
|
PlayerList defendersList = playersAttackedThisTurn.getOrDefault(attacker, null);
|
||||||
|
PlayerList planeswalkerControllersList = planeswalkerControllerAttackedThisTurn.getOrDefault(attacker, null);
|
||||||
|
return (defendersList != null && defendersList.contains(defender)) ||
|
||||||
|
(planeswalkerControllersList != null && planeswalkerControllersList.contains(defender));
|
||||||
|
}
|
||||||
|
|
||||||
public int getAttackedPlayersCount(UUID playerID) {
|
public int getAttackedPlayersCount(UUID playerID) {
|
||||||
PlayerList defendersList = playersAttackedThisTurn.getOrDefault(playerID, null);
|
PlayerList defendersList = playersAttackedThisTurn.getOrDefault(playerID, null);
|
||||||
|
|
@ -79,4 +100,12 @@ public class PlayersAttackedThisTurnWatcher extends Watcher {
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
super.reset();
|
||||||
|
playersAttackedThisTurn.clear();
|
||||||
|
opponentsAttackedThisTurn.clear();
|
||||||
|
planeswalkerControllerAttackedThisTurn.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue