Fixed various potential null pointer exceptions

This commit is contained in:
Evan Kranzler 2018-05-31 21:03:40 -04:00
parent 86f7a663b4
commit ab3cd76d3e
44 changed files with 438 additions and 439 deletions

View file

@ -132,7 +132,7 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect {
// according to the following mail response from MTG Rules Management about False Orders: // according to the following mail response from MTG Rules Management about False Orders:
// "if Player A attacks Players B and C, Player B's creatures cannot block creatures attacking Player C" // "if Player A attacks Players B and C, Player B's creatures cannot block creatures attacking Player C"
// therefore we need to single out creatures attacking the target blocker's controller (disappointing, I know) // therefore we need to single out creatures attacking the target blocker's controller (disappointing, I know)
List<Permanent> list = new ArrayList<>(); List<Permanent> list = new ArrayList<>();
for (CombatGroup combatGroup : game.getCombat().getGroups()) { for (CombatGroup combatGroup : game.getCombat().getGroups()) {
if (combatGroup.getDefendingPlayerId().equals(permanent.getControllerId())) { if (combatGroup.getDefendingPlayerId().equals(permanent.getControllerId())) {
@ -155,7 +155,7 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect {
return true; return true;
} }
Permanent chosenPermanent = game.getPermanent(target.getFirstTarget()); Permanent chosenPermanent = game.getPermanent(target.getFirstTarget());
if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) { if (chosenPermanent != null && chosenPermanent.isCreature()) {
CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId()); CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId());
if (chosenGroup != null) { if (chosenGroup != null) {
// Relevant ruling for Balduvian Warlord: // Relevant ruling for Balduvian Warlord:

View file

@ -59,7 +59,7 @@ import mage.util.CardUtil;
public final class BaronVonCount extends CardImpl { public final class BaronVonCount extends CardImpl {
public BaronVonCount(UUID ownerId, CardSetInfo setInfo) { public BaronVonCount(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{R}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{R}");
addSuperType(SuperType.LEGENDARY); addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.VILLAIN); this.subtype.add(SubType.VILLAIN);
@ -152,22 +152,23 @@ class BaronVonCountTriggeredAbility extends TriggeredAbilityImpl {
Permanent sourcePermanent = game.getPermanent(getSourceId()); Permanent sourcePermanent = game.getPermanent(getSourceId());
MageObject mageObject = game.getObject(getSourceId()); MageObject mageObject = game.getObject(getSourceId());
Spell spell = game.getStack().getSpell(event.getTargetId()); Spell spell = game.getStack().getSpell(event.getTargetId());
if (game.getState().getValue(mageObject.getId() + "_doom") == null) { if (spell == null || sourcePermanent == null || mageObject == null) {
return false; return false;
} }
Integer doomNumber = (Integer) game.getState().getValue(mageObject.getId() + "_doom"); Integer doomNumber = (Integer) game.getState().getValue(mageObject.getId() + "_doom");
if (spell != null && sourcePermanent != null && mageObject != null && doomNumber > 0) { if (doomNumber == null || doomNumber == 0) {
if (!spell.isFaceDown(game)) { return false;
String doomString = doomNumber.toString(); }
if (spell.getCard().getManaCost().getText().contains(doomString) if (!spell.isFaceDown(game)) {
|| String.valueOf(spell.getPower().getBaseValue()).contains(doomString) String doomString = doomNumber.toString();
|| String.valueOf(spell.getToughness().getBaseValue()).contains(doomString)) { if (spell.getCard().getManaCost().getText().contains(doomString)
return true; || String.valueOf(spell.getPower().getBaseValue()).contains(doomString)
} else { || String.valueOf(spell.getToughness().getBaseValue()).contains(doomString)) {
for (String string : spell.getCard().getRules()) { return true;
if (string.contains(doomString)) { } else {
return true; for (String string : spell.getCard().getRules()) {
} if (string.contains(doomString)) {
return true;
} }
} }
} }

View file

@ -56,7 +56,7 @@ import mage.target.common.TargetControlledCreaturePermanent;
public final class BloodfireInfusion extends CardImpl { public final class BloodfireInfusion extends CardImpl {
public BloodfireInfusion(UUID ownerId, CardSetInfo setInfo) { public BloodfireInfusion(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{R}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}");
this.subtype.add(SubType.AURA); this.subtype.add(SubType.AURA);
// Enchant creature you control // Enchant creature you control
@ -65,14 +65,14 @@ public final class BloodfireInfusion extends CardImpl {
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
Ability ability = new EnchantAbility(auraTarget.getTargetName()); Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability); this.addAbility(ability);
// {R}, Sacrifice enchanted creature: Bloodfire Infusion deals damage equal to the sacrificed creature's power to each creature. // {R}, Sacrifice enchanted creature: Bloodfire Infusion deals damage equal to the sacrificed creature's power to each creature.
Effect effect = new DamageAllEffect(new AttachedPermanentPowerCount(), new FilterCreaturePermanent()); Effect effect = new DamageAllEffect(new AttachedPermanentPowerCount(), new FilterCreaturePermanent());
effect.setText("{this} deals damage equal to the sacrificed creature's power to each creature"); effect.setText("{this} deals damage equal to the sacrificed creature's power to each creature");
Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{R}")); Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{R}"));
ability2.addCost(new SacrificeAttachedCost()); ability2.addCost(new SacrificeAttachedCost());
this.addAbility(ability2); this.addAbility(ability2);
} }
public BloodfireInfusion(final BloodfireInfusion card) { public BloodfireInfusion(final BloodfireInfusion card) {
@ -90,10 +90,11 @@ class AttachedPermanentPowerCount implements DynamicValue {
@Override @Override
public int calculate(Game game, Ability sourceAbility, Effect effect) { public int calculate(Game game, Ability sourceAbility, Effect effect) {
Permanent attachment = game.getPermanentOrLKIBattlefield(sourceAbility.getSourceId()); Permanent attachment = game.getPermanentOrLKIBattlefield(sourceAbility.getSourceId());
if (attachment == null) {
return 0;
}
Permanent permanent = game.getPermanentOrLKIBattlefield(attachment.getAttachedTo()); Permanent permanent = game.getPermanentOrLKIBattlefield(attachment.getAttachedTo());
if (attachment != null if (permanent != null && (permanent.getPower().getValue() >= 0)) {
&& permanent != null
&& (permanent.getPower().getValue() >= 0)) {
return permanent.getPower().getValue(); return permanent.getPower().getValue();
} }
return 0; return 0;

View file

@ -86,10 +86,12 @@ class NumberOfCapitalsInTextOfTargetCreatureCount implements DynamicValue {
if (cards != null) { if (cards != null) {
for (CardInfo cardInfo : cards) { for (CardInfo cardInfo : cards) {
Card dummy = cardInfo != null ? cardInfo.getCard() : null; Card dummy = cardInfo != null ? cardInfo.getCard() : null;
for (String line : dummy.getRules()) { if (dummy != null) {
line = line.replaceAll("(?i)<i.*?</i>", ""); // Ignoring reminder text in italic for (String line : dummy.getRules()) {
line = line.replaceAll("\\{this\\}", permanent.getName()); line = line.replaceAll("(?i)<i.*?</i>", ""); // Ignoring reminder text in italic
capitals += line.length() - line.replaceAll("[A-Z]", "").length(); line = line.replaceAll("\\{this\\}", permanent.getName());
capitals += line.length() - line.replaceAll("[A-Z]", "").length();
}
} }
return -1 * capitals; return -1 * capitals;
} }

View file

@ -92,7 +92,7 @@ class CloneShellEffect extends OneShotEffect {
@Override @Override
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) {
return false; return false;
} }
Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 4)); Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 4));

View file

@ -103,46 +103,47 @@ class CrownOfTheAgesEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
UUID auraId = getTargetPointer().getFirst(game, source); Permanent aura = game.getPermanent(source.getFirstTarget());
Permanent aura = game.getPermanent(auraId); if (aura == null) {
return false;
}
Permanent fromPermanent = game.getPermanent(aura.getAttachedTo()); Permanent fromPermanent = game.getPermanent(aura.getAttachedTo());
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (fromPermanent != null && controller != null) { if (fromPermanent == null || controller == null) {
boolean passed = true; return false;
FilterCreaturePermanent filterChoice = new FilterCreaturePermanent("another creature"); }
filterChoice.add(Predicates.not(new PermanentIdPredicate(fromPermanent.getId()))); boolean passed = true;
FilterCreaturePermanent filterChoice = new FilterCreaturePermanent("another creature");
filterChoice.add(Predicates.not(new PermanentIdPredicate(fromPermanent.getId())));
Target chosenCreatureToAttachAura = new TargetPermanent(filterChoice); Target chosenCreatureToAttachAura = new TargetPermanent(filterChoice);
chosenCreatureToAttachAura.setNotTarget(true); chosenCreatureToAttachAura.setNotTarget(true);
if (chosenCreatureToAttachAura.canChoose(source.getSourceId(), source.getControllerId(), game) if (chosenCreatureToAttachAura.canChoose(source.getSourceId(), source.getControllerId(), game)
&& controller.choose(Outcome.Neutral, chosenCreatureToAttachAura, source.getSourceId(), game)) { && controller.choose(Outcome.Neutral, chosenCreatureToAttachAura, source.getSourceId(), game)) {
Permanent creatureToAttachAura = game.getPermanent(chosenCreatureToAttachAura.getFirstTarget()); Permanent creatureToAttachAura = game.getPermanent(chosenCreatureToAttachAura.getFirstTarget());
if (creatureToAttachAura != null) { if (creatureToAttachAura != null) {
if (aura != null && passed) { if (passed) {
// Check the target filter // Check the target filter
Target target = aura.getSpellAbility().getTargets().get(0); Target target = aura.getSpellAbility().getTargets().get(0);
if (target instanceof TargetPermanent) { if (target instanceof TargetPermanent) {
if (!target.getFilter().match(creatureToAttachAura, game)) { if (!target.getFilter().match(creatureToAttachAura, game)) {
passed = false;
}
}
// Check for protection
MageObject auraObject = game.getObject(auraId);
if (creatureToAttachAura.cantBeAttachedBy(auraObject, game)) {
passed = false; passed = false;
} }
} }
if (passed) { // Check for protection
fromPermanent.removeAttachment(aura.getId(), game); MageObject auraObject = game.getObject(aura.getId());
creatureToAttachAura.addAttachment(aura.getId(), game); if (creatureToAttachAura.cantBeAttachedBy(auraObject, game)) {
return true; passed = false;
} }
} }
if (passed) {
fromPermanent.removeAttachment(aura.getId(), game);
creatureToAttachAura.addAttachment(aura.getId(), game);
return true;
}
} }
return true;
} }
return true;
return false;
} }
} }

View file

@ -71,7 +71,7 @@ public final class CrypticGateway extends CardImpl {
TargetControlledPermanent target; TargetControlledPermanent target;
public CrypticGateway(UUID ownerId, CardSetInfo setInfo) { public CrypticGateway(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}");
// Tap two untapped creatures you control: You may put a creature card from your hand that shares a creature type with each creature tapped this way onto the battlefield. // Tap two untapped creatures you control: You may put a creature card from your hand that shares a creature type with each creature tapped this way onto the battlefield.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CrypticGatewayEffect(), new CrypticGatewayCost(new TargetControlledPermanent(filter)))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CrypticGatewayEffect(), new CrypticGatewayCost(new TargetControlledPermanent(filter))));
@ -173,7 +173,7 @@ class CrypticGatewayEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
if (source == null || source.getCosts() == null) { if (source.getCosts() == null) {
return false; return false;
} }

View file

@ -112,7 +112,7 @@ class DrainPowerEffect extends OneShotEffect {
for (Ability ability : permanent.getAbilities()) { for (Ability ability : permanent.getAbilities()) {
if (ability instanceof ActivatedAbility && ability.getAbilityType() == AbilityType.MANA) { if (ability instanceof ActivatedAbility && ability.getAbilityType() == AbilityType.MANA) {
ActivatedManaAbilityImpl manaAbility = (ActivatedManaAbilityImpl) ability; ActivatedManaAbilityImpl manaAbility = (ActivatedManaAbilityImpl) ability;
if (manaAbility != null && manaAbility.canActivate(targetPlayer.getId(), game).canActivate()) { if (manaAbility.canActivate(targetPlayer.getId(), game).canActivate()) {
// canActivate can't check for mana abilities that require a mana cost, if the payment isn't possible (Cabal Coffers etc) // canActivate can't check for mana abilities that require a mana cost, if the payment isn't possible (Cabal Coffers etc)
// so it's necessary to filter them out manually - might be buggy in some fringe cases // so it's necessary to filter them out manually - might be buggy in some fringe cases
for (ManaCost manaCost : manaAbility.getManaCosts()) { for (ManaCost manaCost : manaAbility.getManaCosts()) {

View file

@ -98,44 +98,47 @@ class BecomesColorOrColorsEnchantedEffect 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());
Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (enchantment == null) {
return false;
}
Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); Permanent permanent = game.getPermanent(enchantment.getAttachedTo());
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (controller != null && enchantment != null && permanent != null) { if (controller == null || permanent == null) {
for (int i = 0; i < 5; i++) { return false;
if (i > 0) { }
if (!controller.chooseUse(Outcome.Neutral, "Do you wish to choose another color?", source, game)) { for (int i = 0; i < 5; i++) {
break; if (i > 0) {
} if (!controller.chooseUse(Outcome.Neutral, "Do you wish to choose another color?", source, game)) {
} break;
ChoiceColor choiceColor = new ChoiceColor();
if (!controller.choose(Outcome.Benefit, choiceColor, game)) {
return false;
}
if (!game.isSimulation()) {
game.informPlayers(permanent.getName() + ": " + controller.getLogName() + " has chosen " + choiceColor.getChoice());
}
if (choiceColor.getColor().isBlack()) {
sb.append('B');
} else if (choiceColor.getColor().isBlue()) {
sb.append('U');
} else if (choiceColor.getColor().isRed()) {
sb.append('R');
} else if (choiceColor.getColor().isGreen()) {
sb.append('G');
} else if (choiceColor.getColor().isWhite()) {
sb.append('W');
} }
} }
String colors = new String(sb); ChoiceColor choiceColor = new ChoiceColor();
ObjectColor chosenColors = new ObjectColor(colors); if (!controller.choose(Outcome.Benefit, choiceColor, game)) {
ContinuousEffect effect = new BecomesColorTargetEffect(chosenColors, Duration.Custom); return false;
effect.setTargetPointer(new FixedTarget(permanent.getId())); }
game.addEffect(effect, source); if (!game.isSimulation()) {
game.informPlayers(permanent.getName() + ": " + controller.getLogName() + " has chosen " + choiceColor.getChoice());
return true; }
if (choiceColor.getColor().isBlack()) {
sb.append('B');
} else if (choiceColor.getColor().isBlue()) {
sb.append('U');
} else if (choiceColor.getColor().isRed()) {
sb.append('R');
} else if (choiceColor.getColor().isGreen()) {
sb.append('G');
} else if (choiceColor.getColor().isWhite()) {
sb.append('W');
}
} }
return false; String colors = new String(sb);
ObjectColor chosenColors = new ObjectColor(colors);
ContinuousEffect effect = new BecomesColorTargetEffect(chosenColors, Duration.Custom);
effect.setTargetPointer(new FixedTarget(permanent.getId()));
game.addEffect(effect, source);
return true;
} }
@Override @Override

View file

@ -121,85 +121,87 @@ class FalseOrdersUnblockEffect 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());
Permanent permanent = game.getPermanent(source.getTargets().getFirstTarget()); Permanent permanent = game.getPermanent(source.getTargets().getFirstTarget());
if (controller != null && permanent != null) { if (controller == null || permanent == null) {
return false;
}
// Remove target creature from combat // Remove target creature from combat
Effect effect = new RemoveFromCombatTargetEffect(); Effect effect = new RemoveFromCombatTargetEffect();
effect.apply(game, source); effect.apply(game, source);
// Make blocked creatures unblocked // Make blocked creatures unblocked
BlockedByOnlyOneCreatureThisCombatWatcher watcher = (BlockedByOnlyOneCreatureThisCombatWatcher) game.getState().getWatchers().get(BlockedByOnlyOneCreatureThisCombatWatcher.class.getSimpleName()); BlockedByOnlyOneCreatureThisCombatWatcher watcher = (BlockedByOnlyOneCreatureThisCombatWatcher) game.getState().getWatchers().get(BlockedByOnlyOneCreatureThisCombatWatcher.class.getSimpleName());
if (watcher != null) { if (watcher != null) {
Set<CombatGroup> combatGroups = watcher.getBlockedOnlyByCreature(permanent.getId()); Set<CombatGroup> combatGroups = watcher.getBlockedOnlyByCreature(permanent.getId());
if (combatGroups != null) { if (combatGroups != null) {
for (CombatGroup combatGroup : combatGroups) { for (CombatGroup combatGroup : combatGroups) {
if (combatGroup != null) { if (combatGroup != null) {
combatGroup.setBlocked(false, game); combatGroup.setBlocked(false, game);
}
} }
} }
} }
// Choose new creature to block
if (permanent.isCreature()) {
if (controller.chooseUse(Outcome.Benefit, "Do you want " + permanent.getLogName() + " to block an attacking creature?", source, game)) {
// according to the following mail response from MTG Rules Management about False Orders:
// "if Player A attacks Players B and C, Player B's creatures cannot block creatures attacking Player C"
// therefore we need to single out creatures attacking the target blocker's controller (disappointing, I know)
List<Permanent> list = new ArrayList<>();
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
if (combatGroup.getDefendingPlayerId().equals(permanent.getControllerId())) {
for (UUID attackingCreatureId : combatGroup.getAttackers()) {
Permanent targetsControllerAttacker = game.getPermanent(attackingCreatureId);
list.add(targetsControllerAttacker);
}
}
}
Player targetsController = game.getPlayer(permanent.getControllerId());
if (targetsController != null) {
FilterAttackingCreature filter = new FilterAttackingCreature("creature attacking " + targetsController.getLogName());
filter.add(new PermanentInListPredicate(list));
TargetAttackingCreature target = new TargetAttackingCreature(1, 1, filter, true);
if (target.canChoose(source.getSourceId(), controller.getId(), game)) {
while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
} else {
return true;
}
Permanent chosenPermanent = game.getPermanent(target.getFirstTarget());
if (chosenPermanent != null && permanent != null && chosenPermanent.isCreature() && controller != null) {
CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId());
if (chosenGroup != null) {
// Relevant ruling for Balduvian Warlord:
// 7/15/2006 If an attacking creature has an ability that triggers When this creature becomes blocked,
// it triggers when a creature blocks it due to the Warlords ability only if it was unblocked at that point.
boolean notYetBlocked = chosenGroup.getBlockers().isEmpty();
chosenGroup.addBlockerToGroup(permanent.getId(), controller.getId(), game);
game.getCombat().addBlockingGroup(permanent.getId(), chosenPermanent.getId(), controller.getId(), game); // 702.21h
if (notYetBlocked) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null));
for (UUID bandedId : chosenPermanent.getBandedCards()) {
CombatGroup bandedGroup = game.getCombat().findGroup(bandedId);
if (bandedGroup != null && chosenGroup.getBlockers().size() == 1) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, bandedId, null));
}
}
}
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
}
CombatGroup blockGroup = findBlockingGroup(permanent, game); // a new blockingGroup is formed, so it's necessary to find it again
if (blockGroup != null) {
blockGroup.pickAttackerOrder(permanent.getControllerId(), game);
}
}
}
}
return true;
}
} }
return false;
if (!permanent.isCreature()
|| !controller.chooseUse(Outcome.Benefit, "Do you want " + permanent.getLogName() + " to block an attacking creature?", source, game)) {
return false;
}
// Choose new creature to block
// according to the following mail response from MTG Rules Management about False Orders:
// "if Player A attacks Players B and C, Player B's creatures cannot block creatures attacking Player C"
// therefore we need to single out creatures attacking the target blocker's controller (disappointing, I know)
List<Permanent> list = new ArrayList<>();
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
if (combatGroup.getDefendingPlayerId().equals(permanent.getControllerId())) {
for (UUID attackingCreatureId : combatGroup.getAttackers()) {
Permanent targetsControllerAttacker = game.getPermanent(attackingCreatureId);
list.add(targetsControllerAttacker);
}
}
}
Player targetsController = game.getPlayer(permanent.getControllerId());
if (targetsController == null) {
return false;
}
FilterAttackingCreature filter = new FilterAttackingCreature("creature attacking " + targetsController.getLogName());
filter.add(new PermanentInListPredicate(list));
TargetAttackingCreature target = new TargetAttackingCreature(1, 1, filter, true);
if (target.canChoose(source.getSourceId(), controller.getId(), game)) {
while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
} else {
return true;
}
Permanent chosenPermanent = game.getPermanent(target.getFirstTarget());
if (chosenPermanent == null || !chosenPermanent.isCreature()) {
return false;
}
CombatGroup chosenGroup = game.getCombat().findGroup(chosenPermanent.getId());
if (chosenGroup != null) {
// Relevant ruling for Balduvian Warlord:
// 7/15/2006 If an attacking creature has an ability that triggers When this creature becomes blocked,
// it triggers when a creature blocks it due to the Warlords ability only if it was unblocked at that point.
boolean notYetBlocked = chosenGroup.getBlockers().isEmpty();
chosenGroup.addBlockerToGroup(permanent.getId(), controller.getId(), game);
game.getCombat().addBlockingGroup(permanent.getId(), chosenPermanent.getId(), controller.getId(), game); // 702.21h
if (notYetBlocked) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, chosenPermanent.getId(), null));
for (UUID bandedId : chosenPermanent.getBandedCards()) {
CombatGroup bandedGroup = game.getCombat().findGroup(bandedId);
if (bandedGroup != null && chosenGroup.getBlockers().size() == 1) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.CREATURE_BLOCKED, bandedId, null));
}
}
}
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, chosenPermanent.getId(), permanent.getId(), permanent.getControllerId()));
}
CombatGroup blockGroup = findBlockingGroup(permanent, game); // a new blockingGroup is formed, so it's necessary to find it again
if (blockGroup != null) {
blockGroup.pickAttackerOrder(permanent.getControllerId(), game);
}
return true;
} }
private CombatGroup findBlockingGroup(Permanent blocker, Game game) { private CombatGroup findBlockingGroup(Permanent blocker, Game game) {

View file

@ -51,14 +51,14 @@ import mage.target.common.TargetCreaturePermanent;
* @author jeffwadsworth * @author jeffwadsworth
*/ */
public final class GildedCerodon extends CardImpl { public final class GildedCerodon extends CardImpl {
private static final String rule = "Whenever {this} attacks, if you control a Desert or there is a Desert card in your graveyard, target creature can't block this turn."; private static final String rule = "Whenever {this} attacks, if you control a Desert or there is a Desert card in your graveyard, target creature can't block this turn.";
public GildedCerodon(UUID ownerId, CardSetInfo setInfo) { public GildedCerodon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}");
this.subtype.add(SubType.BEAST); this.subtype.add(SubType.BEAST);
this.power = new MageInt(4); this.power = new MageInt(4);
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
@ -66,7 +66,7 @@ public final class GildedCerodon extends CardImpl {
Ability ability = new ConditionalTriggeredAbility(new AttacksTriggeredAbility(new CantBlockTargetEffect(Duration.EndOfTurn), false), new GildedCerodonCondition(), rule); Ability ability = new ConditionalTriggeredAbility(new AttacksTriggeredAbility(new CantBlockTargetEffect(Duration.EndOfTurn), false), new GildedCerodonCondition(), rule);
ability.addTarget(new TargetCreaturePermanent()); ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability); this.addAbility(ability);
} }
public GildedCerodon(final GildedCerodon card) { public GildedCerodon(final GildedCerodon card) {
@ -80,10 +80,10 @@ public final class GildedCerodon extends CardImpl {
} }
class GildedCerodonCondition implements Condition { class GildedCerodonCondition implements Condition {
private static final FilterPermanent filter = new FilterPermanent(); private static final FilterPermanent filter = new FilterPermanent();
private static final FilterCard filter2 = new FilterCard(); private static final FilterCard filter2 = new FilterCard();
static { static {
filter.add(new SubtypePredicate(SubType.DESERT)); filter.add(new SubtypePredicate(SubType.DESERT));
filter2.add(new SubtypePredicate(SubType.DESERT)); filter2.add(new SubtypePredicate(SubType.DESERT));
@ -93,8 +93,8 @@ class GildedCerodonCondition implements Condition {
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
&& !game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).isEmpty() && (!game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game).isEmpty()
|| controller.getGraveyard().count(filter2, game) > 0) { || controller.getGraveyard().count(filter2, game) > 0)) {
return true; return true;
} }
return false; return false;

View file

@ -98,6 +98,10 @@ class GlissaSunseekerEffect extends OneShotEffect {
@Override @Override
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());
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (controller == null || permanent == null) {
return false;
}
ManaPool pool = controller.getManaPool(); ManaPool pool = controller.getManaPool();
int blackMana = pool.getBlack(); int blackMana = pool.getBlack();
int whiteMana = pool.getWhite(); int whiteMana = pool.getWhite();
@ -106,11 +110,8 @@ class GlissaSunseekerEffect extends OneShotEffect {
int redMana = pool.getRed(); int redMana = pool.getRed();
int colorlessMana = pool.getColorless(); int colorlessMana = pool.getColorless();
int manaPoolTotal = blackMana + whiteMana + blueMana + greenMana + redMana + colorlessMana; int manaPoolTotal = blackMana + whiteMana + blueMana + greenMana + redMana + colorlessMana;
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent.getConvertedManaCost() == manaPoolTotal) {
if (controller != null && permanent != null) { return permanent.destroy(source.getSourceId(), game, false);
if (permanent.getConvertedManaCost() == manaPoolTotal) {
return permanent.destroy(source.getSourceId(), game, false);
}
} }
return false; return false;
} }

View file

@ -51,7 +51,7 @@ import mage.players.Player;
public final class GoblinPsychopath extends CardImpl { public final class GoblinPsychopath extends CardImpl {
public GoblinPsychopath(UUID ownerId, CardSetInfo setInfo) { public GoblinPsychopath(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
this.subtype.add(SubType.GOBLIN); this.subtype.add(SubType.GOBLIN);
this.subtype.add(SubType.MUTANT); this.subtype.add(SubType.MUTANT);
this.power = new MageInt(5); this.power = new MageInt(5);
@ -72,7 +72,7 @@ public final class GoblinPsychopath extends CardImpl {
} }
class GoblinPsychopathEffect extends ReplacementEffectImpl { class GoblinPsychopathEffect extends ReplacementEffectImpl {
private boolean wonFlip; private boolean wonFlip;
public GoblinPsychopathEffect() { public GoblinPsychopathEffect() {
@ -97,9 +97,9 @@ class GoblinPsychopathEffect extends ReplacementEffectImpl {
@Override @Override
public boolean checksEventType(GameEvent event, Game game) { public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || return event.getType() == GameEvent.EventType.DAMAGE_CREATURE
event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || || event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER
event.getType() == GameEvent.EventType.DAMAGE_PLAYER; || event.getType() == GameEvent.EventType.DAMAGE_PLAYER;
} }
@Override @Override
@ -116,21 +116,19 @@ class GoblinPsychopathEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) { public boolean replaceEvent(GameEvent event, Ability source, Game game) {
MageObject object = game.getObject(event.getSourceId()); MageObject object = game.getObject(event.getSourceId());
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null && object != null) { if (controller == null || object == null
if (this.applies(event, source, game) && event instanceof DamageEvent && event.getAmount() > 0) { || !(this.applies(event, source, game) && event instanceof DamageEvent && event.getAmount() > 0)) {
DamageEvent damageEvent = (DamageEvent) event; return false;
if (damageEvent.isCombatDamage()) {
if (!wonFlip) {
// TODO: make this redirect damage from all blockers
controller.damage(event.getAmount(), source.getSourceId(), game, false, true);
String sourceLogName = source != null ? game.getObject(source.getSourceId()).getLogName() + ": " : "";
game.informPlayers(sourceLogName + "Redirected " + event.getAmount() + " damage to " + controller.getLogName());
this.discard();
return true;
}
}
}
} }
return false; DamageEvent damageEvent = (DamageEvent) event;
if (!damageEvent.isCombatDamage() || wonFlip) {
return false;
}
// TODO: make this redirect damage from all blockers
controller.damage(event.getAmount(), source.getSourceId(), game, false, true);
String sourceLogName = game.getObject(source.getSourceId()).getLogName() + ": ";
game.informPlayers(sourceLogName + "Redirected " + event.getAmount() + " damage to " + controller.getLogName());
this.discard();
return true;
} }
} }

View file

@ -51,7 +51,7 @@ import mage.target.common.TargetLandPermanent;
public final class Helldozer extends CardImpl { public final class Helldozer extends CardImpl {
public Helldozer(UUID ownerId, CardSetInfo setInfo) { public Helldozer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}{B}");
this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.ZOMBIE);
this.subtype.add(SubType.GIANT); this.subtype.add(SubType.GIANT);
@ -96,15 +96,14 @@ class HelldozerEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent helldozer = game.getPermanent(source.getSourceId()); Permanent helldozer = game.getPermanent(source.getSourceId());
Permanent landTarget = game.getPermanent(source.getFirstTarget()); Permanent landTarget = game.getPermanent(source.getFirstTarget());
if (landTarget != null) { if (landTarget == null) {
landTarget.destroy(id, game, false); return false;
} }
Permanent landPermanent = (Permanent) game.getLastKnownInformation(landTarget.getId(), Zone.BATTLEFIELD); boolean wasNonBasic = !landTarget.isBasic();
if (landPermanent != null landTarget.destroy(id, game, false);
&& !landPermanent.isBasic() if (wasNonBasic && helldozer != null) {
&& helldozer != null) {
return helldozer.untap(game); return helldozer.untap(game);
} }
return false; return false;
} }
} }

View file

@ -61,15 +61,16 @@ import mage.target.targetpointer.FixedTarget;
public final class Heroism extends CardImpl { public final class Heroism extends CardImpl {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a white creature"); private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a white creature");
static { static {
filter.add(new ColorPredicate(ObjectColor.WHITE)); filter.add(new ColorPredicate(ObjectColor.WHITE));
} }
public Heroism(UUID ownerId, CardSetInfo setInfo) { public Heroism(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
// Sacrifice a white creature: For each attacking red creature, prevent all combat damage that would be dealt by that creature this turn unless its controller pays {2}{R}. // Sacrifice a white creature: For each attacking red creature, prevent all combat damage that would be dealt by that creature this turn unless its controller pays {2}{R}.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new HeroismEffect(), new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1, filter, true)))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new HeroismEffect(), new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, true))));
} }
public Heroism(final Heroism card) { public Heroism(final Heroism card) {
@ -83,8 +84,9 @@ public final class Heroism extends CardImpl {
} }
class HeroismEffect extends OneShotEffect { class HeroismEffect extends OneShotEffect {
private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking red creature"); private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking red creature");
static { static {
filter.add(new ColorPredicate(ObjectColor.RED)); filter.add(new ColorPredicate(ObjectColor.RED));
} }
@ -111,7 +113,7 @@ class HeroismEffect extends OneShotEffect {
Player player = game.getPlayer(game.getActivePlayerId()); Player player = game.getPlayer(game.getActivePlayerId());
Cost cost = new ManaCostsImpl("{2}{R}"); Cost cost = new ManaCostsImpl("{2}{R}");
List<Permanent> permanentsToPrevent = new ArrayList<>(); List<Permanent> permanentsToPrevent = new ArrayList<>();
for (Permanent permanent : game.getState().getBattlefield().getAllActivePermanents(filter, player.getId(), game)) { for (Permanent permanent : game.getState().getBattlefield().getAllActivePermanents(filter, game.getActivePlayerId(), game)) {
cost.clearPaid(); cost.clearPaid();
String message = "Pay " + cost.getText() + "? If you don't, " + permanent.getLogName() + "'s combat damage will be prevented this turn."; String message = "Pay " + cost.getText() + "? If you don't, " + permanent.getLogName() + "'s combat damage will be prevented this turn.";
if (player != null && player.chooseUse(Outcome.Neutral, message, source, game)) { if (player != null && player.chooseUse(Outcome.Neutral, message, source, game)) {

View file

@ -48,12 +48,10 @@ import mage.players.Player;
public final class ImmortalServitude extends CardImpl { public final class ImmortalServitude extends CardImpl {
public ImmortalServitude(UUID ownerId, CardSetInfo setInfo) { public ImmortalServitude(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{W/B}{W/B}{W/B}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{W/B}{W/B}{W/B}");
// Return each creature card with converted mana cost X from your graveyard to the battlefield. // Return each creature card with converted mana cost X from your graveyard to the battlefield.
this.getSpellAbility().addEffect(new ImmortalServitudeEffect()); this.getSpellAbility().addEffect(new ImmortalServitudeEffect());
} }
public ImmortalServitude(final ImmortalServitude card) { public ImmortalServitude(final ImmortalServitude card) {
@ -88,11 +86,10 @@ class ImmortalServitudeEffect extends OneShotEffect {
int count = source.getManaCostsToPay().getX(); int count = source.getManaCostsToPay().getX();
Set<Card> cards = you.getGraveyard().getCards(new FilterCreatureCard(), game); Set<Card> cards = you.getGraveyard().getCards(new FilterCreatureCard(), game);
for (Card card : cards) { for (Card card : cards) {
if (card.getConvertedManaCost() == count if (card != null && card.getConvertedManaCost() == count) {
&& card != null) {
card.moveToZone(Zone.BATTLEFIELD, source.getSourceId(), game, false); card.moveToZone(Zone.BATTLEFIELD, source.getSourceId(), game, false);
} }
} }
return true; return true;
} }
} }

View file

@ -47,7 +47,7 @@ import mage.players.Player;
public final class IronMaiden extends CardImpl { public final class IronMaiden extends CardImpl {
public IronMaiden(UUID ownerId, CardSetInfo setInfo) { public IronMaiden(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// At the beginning of each opponent's upkeep, Iron Maiden deals X damage to that player, where X is the number of cards in their hand minus 4. // At the beginning of each opponent's upkeep, Iron Maiden deals X damage to that player, where X is the number of cards in their hand minus 4.
Ability ability = new BeginningOfUpkeepTriggeredAbility(new IronMaidenEffect(), TargetController.OPPONENT, false); Ability ability = new BeginningOfUpkeepTriggeredAbility(new IronMaidenEffect(), TargetController.OPPONENT, false);
@ -62,10 +62,8 @@ public final class IronMaiden extends CardImpl {
public IronMaiden copy() { public IronMaiden copy() {
return new IronMaiden(this); return new IronMaiden(this);
} }
}
}
class IronMaidenEffect extends OneShotEffect { class IronMaidenEffect extends OneShotEffect {
@ -80,16 +78,11 @@ class IronMaidenEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(targetPointer.getFirst(game, source)); Player player = game.getPlayer(targetPointer.getFirst(game, source));
if(player != null) if (player != null) {
{
int amount = player.getHand().size() - 4; int amount = player.getHand().size() - 4;
if(amount > 0) if (amount > 0) {
{ player.damage(amount, source.getSourceId(), game, false, true);
if (player != null) { return true;
player.damage(amount, source.getSourceId(), game, false, true);
return true;
}
} }
} }
return false; return false;
@ -99,7 +92,7 @@ class IronMaidenEffect extends OneShotEffect {
public IronMaidenEffect copy() { public IronMaidenEffect copy() {
return new IronMaidenEffect(this); return new IronMaidenEffect(this);
} }
@Override @Override
public String getText(Mode mode) { public String getText(Mode mode) {
return "Iron Maiden deals X damage to that player, where X is the number of cards in their hand minus 4"; return "Iron Maiden deals X damage to that player, where X is the number of cards in their hand minus 4";

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.cards.i; package mage.cards.i;
import java.util.UUID; import java.util.UUID;
@ -55,7 +54,7 @@ import mage.target.common.TargetCreaturePermanent;
public final class IronclawCurse extends CardImpl { public final class IronclawCurse extends CardImpl {
public IronclawCurse(UUID ownerId, CardSetInfo setInfo) { public IronclawCurse(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{R}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}");
this.subtype.add(SubType.AURA); this.subtype.add(SubType.AURA);
// Enchant creature // Enchant creature
@ -67,7 +66,7 @@ public final class IronclawCurse extends CardImpl {
// Enchanted creature gets 0/-1. // Enchanted creature gets 0/-1.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(0, -1, Duration.WhileOnBattlefield))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(0, -1, Duration.WhileOnBattlefield)));
// Enchanted creature can't block creatures with power equal to or greater than the enchanted creature's toughness. // Enchanted creature can't block creatures with power equal to or greater than the enchanted creature's toughness.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IronclawCurseEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IronclawCurseEffect()));
} }
@ -101,8 +100,11 @@ class IronclawCurseEffect extends CantBlockAttachedEffect {
@Override @Override
public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) {
Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (enchantment == null) {
return false;
}
Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo());
if (enchantment != null && enchantment.getAttachedTo() != null) { if (enchantment.getAttachedTo() != null) {
return !(attacker.getPower().getValue() >= enchantedCreature.getToughness().getValue()); return !(attacker.getPower().getValue() >= enchantedCreature.getToughness().getValue());
} }
return true; return true;

View file

@ -265,15 +265,14 @@ class KarnPlayerExileEffect extends OneShotEffect {
if (sourceObject == null) { if (sourceObject == null) {
return false; return false;
} }
if (player != null) { if (player == null) {
TargetCardInHand target = new TargetCardInHand(); return false;
if (target != null }
&& target.canChoose(source.getSourceId(), player.getId(), game)) { TargetCardInHand target = new TargetCardInHand();
if (target.chooseTarget(Outcome.Exile, player.getId(), source, game)) { if (target.canChoose(source.getSourceId(), player.getId(), game)
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); && target.chooseTarget(Outcome.Exile, player.getId(), source, game)) {
return player.moveCardsToExile(new CardsImpl(target.getTargets()).getCards(game), source, game, true, exileId, sourceObject.getIdName()); UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
} return player.moveCardsToExile(new CardsImpl(target.getTargets()).getCards(game), source, game, true, exileId, sourceObject.getIdName());
}
} }
return false; return false;
} }

View file

@ -48,8 +48,7 @@ import java.util.*;
public final class KillingWave extends CardImpl { public final class KillingWave extends CardImpl {
public KillingWave(UUID ownerId, CardSetInfo setInfo) { public KillingWave(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{B}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{B}");
// For each creature, its controller sacrifices it unless he or she pays X life. // For each creature, its controller sacrifices it unless he or she pays X life.
this.getSpellAbility().addEffect(new KillingWaveEffect()); this.getSpellAbility().addEffect(new KillingWaveEffect());
@ -96,21 +95,23 @@ class KillingWaveEffect extends OneShotEffect {
FilterCreaturePermanent filter = new FilterCreaturePermanent(); FilterCreaturePermanent filter = new FilterCreaturePermanent();
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId); Player player = game.getPlayer(playerId);
List<Permanent> creatures = game.getBattlefield().getAllActivePermanents(filter, playerId, game); if (player != null) {
List<Permanent> creatures = game.getBattlefield().getAllActivePermanents(filter, playerId, game);
int lifePaid = 0; int lifePaid = 0;
int playerLife = player.getLife(); int playerLife = player.getLife();
for (Permanent creature : creatures) { for (Permanent creature : creatures) {
String message = "Pay " + amount + " life? If you don't, " + creature.getName() + " will be sacrificed."; String message = "Pay " + amount + " life? If you don't, " + creature.getName() + " will be sacrificed.";
if (playerLife - amount - lifePaid >= 0 && player != null && player.chooseUse(Outcome.Neutral, message, source, game)) { if (playerLife - amount - lifePaid >= 0 && player.chooseUse(Outcome.Neutral, message, source, game)) {
game.informPlayers(player.getLogName() + " pays " + amount + " life. He will not sacrifice " + creature.getName()); game.informPlayers(player.getLogName() + " pays " + amount + " life. He will not sacrifice " + creature.getName());
lifePaid += amount; lifePaid += amount;
} else { } else {
game.informPlayers(player.getLogName() + " will sacrifice " + creature.getName()); game.informPlayers(player.getLogName() + " will sacrifice " + creature.getName());
sacrifices.add(creature); sacrifices.add(creature);
}
} }
lifePaidAmounts.put(playerId, lifePaid);
} }
lifePaidAmounts.put(playerId, lifePaid);
} }
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {

View file

@ -57,7 +57,7 @@ import mage.target.targetpointer.FixedTarget;
public final class LastRites extends CardImpl { public final class LastRites extends CardImpl {
public LastRites(UUID ownerId, CardSetInfo setInfo) { public LastRites(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
// Discard any number of cards. Target player reveals their hand, then you choose a nonland card from it for each card discarded this way. That player discards those cards. // Discard any number of cards. Target player reveals their hand, then you choose a nonland card from it for each card discarded this way. That player discards those cards.
this.getSpellAbility().addEffect(new LastRitesEffect()); this.getSpellAbility().addEffect(new LastRitesEffect());
@ -107,16 +107,12 @@ class LastRitesEffect extends OneShotEffect {
controller.discard(card, source, game); controller.discard(card, source, game);
} }
} }
if (targetPlayer != null) { FilterCard filter = new FilterCard((discardCount > 1 ? "" : "a") + " nonland card" + (discardCount > 1 ? "s" : ""));
FilterCard filter = new FilterCard((discardCount > 1 ? "" : "a") + " nonland card" + (discardCount > 1 ? "s" : "")); filter.add(Predicates.not(new CardTypePredicate(CardType.LAND)));
filter.add(Predicates.not(new CardTypePredicate(CardType.LAND))); StaticValue discardValue = new StaticValue(discardCount);
StaticValue discardValue = new StaticValue(discardCount); Effect effect = new DiscardCardYouChooseTargetEffect(discardValue, filter, TargetController.ANY);
Effect effect = new DiscardCardYouChooseTargetEffect(discardValue, filter, TargetController.ANY); effect.setTargetPointer(new FixedTarget(targetPlayer.getId()));
effect.setTargetPointer(new FixedTarget(targetPlayer.getId())); effect.apply(game, source);
effect.apply(game, source);
} else {
return false;
}
} }
return true; return true;
} }

View file

@ -76,7 +76,7 @@ public final class LudevicNecroAlchemist extends CardImpl {
} }
class LudevicNecroAlchemistCondition implements Condition { class LudevicNecroAlchemistCondition implements Condition {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
PlayerLostLifeWatcher watcher = (PlayerLostLifeWatcher) game.getState().getWatchers().get(PlayerLostLifeWatcher.class.getSimpleName()); PlayerLostLifeWatcher watcher = (PlayerLostLifeWatcher) game.getState().getWatchers().get(PlayerLostLifeWatcher.class.getSimpleName());
@ -85,13 +85,14 @@ class LudevicNecroAlchemistCondition implements Condition {
Player currentPlayer = null; Player currentPlayer = null;
UUID sourcePlayerId = source.getControllerId(); UUID sourcePlayerId = source.getControllerId();
Player firstPlayer = null; Player firstPlayer = null;
if (playerList != null) { if (playerList == null) {
firstPlayer = playerList.getCurrent(game); return false;
currentPlayer = playerList.getNext(game);
} }
firstPlayer = playerList.getCurrent(game);
currentPlayer = playerList.getNext(game);
while (watcher != null && currentPlayer != null) { while (watcher != null && currentPlayer != null) {
if (currentPlayer != null && !Objects.equals(currentPlayer.getId(), sourcePlayerId) && watcher.getLiveLost(currentPlayer.getId()) > 0) { if (!Objects.equals(currentPlayer.getId(), sourcePlayerId) && watcher.getLiveLost(currentPlayer.getId()) > 0) {
return true; return true;
} }
if (Objects.equals(currentPlayer, firstPlayer)) { if (Objects.equals(currentPlayer, firstPlayer)) {

View file

@ -60,7 +60,7 @@ import mage.target.targetpointer.FixedTarget;
public final class MammothHarness extends CardImpl { public final class MammothHarness extends CardImpl {
public MammothHarness(UUID ownerId, CardSetInfo setInfo) { public MammothHarness(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{G}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}");
this.subtype.add(SubType.AURA); this.subtype.add(SubType.AURA);
// Enchant creature // Enchant creature
@ -69,7 +69,7 @@ public final class MammothHarness extends CardImpl {
this.getSpellAbility().addEffect(new AttachEffect(Outcome.LoseAbility)); this.getSpellAbility().addEffect(new AttachEffect(Outcome.LoseAbility));
Ability ability = new EnchantAbility(auraTarget.getTargetName()); Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability); this.addAbility(ability);
// Enchanted creature loses flying. // Enchanted creature loses flying.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LoseAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LoseAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA)));
@ -103,20 +103,18 @@ class MammothHarnessTriggeredAbility extends BlocksOrBecomesBlockedTriggeredAbil
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(this.getSourceId()); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(this.getSourceId());
if (sourcePermanent != null) { if (sourcePermanent != null) {
Permanent attachedTo = game.getPermanentOrLKIBattlefield(sourcePermanent.getAttachedTo()); Permanent attachedTo = game.getPermanentOrLKIBattlefield(sourcePermanent.getAttachedTo());
if (sourcePermanent != null) { if (event.getSourceId().equals(attachedTo.getId())) {
if (event.getSourceId().equals(attachedTo.getId())) { Permanent blocked = game.getPermanent(event.getTargetId());
Permanent blocked = game.getPermanent(event.getTargetId()); if (blocked != null && filter.match(blocked, game)) {
if (blocked != null && filter.match(blocked, game)) { this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId()));
this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); return true;
return true;
}
} }
if (event.getTargetId().equals(attachedTo.getId())) { }
Permanent blocker = game.getPermanent(event.getSourceId()); if (event.getTargetId().equals(attachedTo.getId())) {
if (blocker != null) { Permanent blocker = game.getPermanent(event.getSourceId());
this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId())); if (blocker != null) {
return true; this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId()));
} return true;
} }
} }
} }

View file

@ -125,14 +125,12 @@ class MaralenOfTheMornsongEffect2 extends OneShotEffect {
Player player = game.getPlayer(activePlayerId); Player player = game.getPlayer(activePlayerId);
if (player != null) { if (player != null) {
player.loseLife(3, game, false); player.loseLife(3, game, false);
if (player != null) { TargetCardInLibrary target = new TargetCardInLibrary();
TargetCardInLibrary target = new TargetCardInLibrary(); if (player.searchLibrary(target, game)) {
if (player.searchLibrary(target, game)) { player.moveCards(new CardsImpl(target.getTargets()), Zone.HAND, source, game);
player.moveCards(new CardsImpl(target.getTargets()), Zone.HAND, source, game);
}
player.shuffleLibrary(source, game);
return true;
} }
player.shuffleLibrary(source, game);
return true;
} }
return false; return false;
} }

View file

@ -97,10 +97,8 @@ class PerishTheThoughtEffect extends OneShotEffect {
if (you.choose(Outcome.Neutral, targetOpponent.getHand(), target, game)) { if (you.choose(Outcome.Neutral, targetOpponent.getHand(), target, game)) {
Card chosenCard = targetOpponent.getHand().get(target.getFirstTarget(), game); Card chosenCard = targetOpponent.getHand().get(target.getFirstTarget(), game);
if (chosenCard != null) { if (chosenCard != null) {
if (targetOpponent != null) { chosenCard.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false);
chosenCard.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false); targetOpponent.shuffleLibrary(source, game);
targetOpponent.shuffleLibrary(source, game);
}
} }
} }
return true; return true;

View file

@ -60,7 +60,7 @@ import mage.target.targetpointer.FixedTarget;
public final class PowerLeak extends CardImpl { public final class PowerLeak extends CardImpl {
public PowerLeak(UUID ownerId, CardSetInfo setInfo) { public PowerLeak(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}");
this.subtype.add(SubType.AURA); this.subtype.add(SubType.AURA);
// Enchant enchantment // Enchant enchantment
@ -103,31 +103,31 @@ class PowerLeakEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(game.getActivePlayerId()); Player player = game.getPlayer(game.getActivePlayerId());
Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (player != null && permanent != null) { if (player == null || permanent == null) {
ManaCosts<ManaCost> cost = new ManaCostsImpl<>("{X}"); return false;
String message = "Pay {X} to prevent X damage from " + permanent.getLogName() + "?"; }
int xValue = 0; ManaCosts<ManaCost> cost = new ManaCostsImpl<>("{X}");
if (player != null && player.chooseUse(Outcome.Neutral, message, source, game)) { String message = "Pay {X} to prevent X damage from " + permanent.getLogName() + "?";
xValue = player.announceXMana(0, Integer.MAX_VALUE, "Choose the amount of mana to pay", game, source); int xValue = 0;
cost.add(new GenericManaCost(xValue)); if (player.chooseUse(Outcome.Neutral, message, source, game)) {
if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) { xValue = player.announceXMana(0, Integer.MAX_VALUE, "Choose the amount of mana to pay", game, source);
game.informPlayers(player.getLogName() + " paid {" + xValue + "} for " + permanent.getLogName()); cost.add(new GenericManaCost(xValue));
} else { if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
game.informPlayers(player.getLogName() + " didn't pay {X} for " + permanent.getLogName()); game.informPlayers(player.getLogName() + " paid {" + xValue + "} for " + permanent.getLogName());
}
} else { } else {
game.informPlayers(player.getLogName() + " didn't pay {X} for " + permanent.getLogName()); game.informPlayers(player.getLogName() + " didn't pay {X} for " + permanent.getLogName());
} }
} else {
PreventDamageByTargetEffect effect = new PreventDamageByTargetEffect(Duration.OneUse, xValue, false); game.informPlayers(player.getLogName() + " didn't pay {X} for " + permanent.getLogName());
if (xValue != 0 && cost.isPaid()) {
effect.setTargetPointer(new FixedTarget(permanent.getId()));
game.addEffect(effect, source);
}
player.damage(2, source.getSourceId(), game, false, true);
effect.discard();
return true;
} }
return false;
PreventDamageByTargetEffect effect = new PreventDamageByTargetEffect(Duration.OneUse, xValue, false);
if (xValue != 0 && cost.isPaid()) {
effect.setTargetPointer(new FixedTarget(permanent.getId()));
game.addEffect(effect, source);
}
player.damage(2, source.getSourceId(), game, false, true);
effect.discard();
return true;
} }
} }

View file

@ -58,7 +58,7 @@ public final class QuietSpeculation extends CardImpl {
} }
public QuietSpeculation(UUID ownerId, CardSetInfo setInfo) { public QuietSpeculation(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{U}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}");
// Search target player's library for up to three cards with flashback and put them into that player's graveyard. Then the player shuffles their library. // Search target player's library for up to three cards with flashback and put them into that player's graveyard. Then the player shuffles their library.
TargetCardInLibrary target = new TargetCardInLibrary(0, 3, filterCard); TargetCardInLibrary target = new TargetCardInLibrary(0, 3, filterCard);
@ -96,7 +96,10 @@ class SearchLibraryPutInGraveEffect extends SearchEffect {
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());
UUID targetPlayerID = source.getFirstTarget(); UUID targetPlayerID = source.getFirstTarget();
if (controller != null && targetPlayerID != null && controller.searchLibrary(target, game, targetPlayerID)) { if (controller == null) {
return false;
}
if (targetPlayerID != null && controller.searchLibrary(target, game, targetPlayerID)) {
if (!target.getTargets().isEmpty()) { if (!target.getTargets().isEmpty()) {
Cards cards = new CardsImpl(target.getTargets()); Cards cards = new CardsImpl(target.getTargets());
controller.revealCards("Quiet Speculation", cards, game); controller.revealCards("Quiet Speculation", cards, game);
@ -108,5 +111,4 @@ class SearchLibraryPutInGraveEffect extends SearchEffect {
controller.shuffleLibrary(source, game); controller.shuffleLibrary(source, game);
return false; return false;
} }
} }

View file

@ -47,7 +47,7 @@ import mage.target.TargetPermanent;
public final class Recoil extends CardImpl { public final class Recoil extends CardImpl {
public Recoil(UUID ownerId, CardSetInfo setInfo) { public Recoil(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{B}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{B}");
// Return target permanent to its owner's hand. Then that player discards a card. // Return target permanent to its owner's hand. Then that player discards a card.
this.getSpellAbility().addEffect(new RecoilEffect()); this.getSpellAbility().addEffect(new RecoilEffect());
@ -84,8 +84,11 @@ class RecoilEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent target = game.getPermanent(source.getFirstTarget()); Permanent target = game.getPermanent(source.getFirstTarget());
if (target == null) {
return false;
}
Player controller = game.getPlayer(target.getControllerId()); Player controller = game.getPlayer(target.getControllerId());
if (target != null && controller != null) { if (controller != null) {
controller.moveCards(target, Zone.HAND, source, game); controller.moveCards(target, Zone.HAND, source, game);
controller.discard(1, false, source, game); controller.discard(1, false, source, game);
return true; return true;

View file

@ -131,10 +131,10 @@ class SavingGraceReplacementEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) { public boolean replaceEvent(GameEvent event, Ability source, Game game) {
DamageEvent damageEvent = (DamageEvent) event; DamageEvent damageEvent = (DamageEvent) event;
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (sourcePermanent != null) { if (sourcePermanent != null) {
Permanent creature = game.getPermanent(sourcePermanent.getAttachedTo()); Permanent creature = game.getPermanent(sourcePermanent.getAttachedTo());
if (creature == null) { if (creature == null) {
return false; return false;
} }
@ -157,10 +157,8 @@ class SavingGraceReplacementEffect extends ReplacementEffectImpl {
} }
game.informPlayers(message.toString()); game.informPlayers(message.toString());
// Redirect damage // Redirect damage
if (creature != null) { creature.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects());
creature.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); return true;
return true;
}
} }
return false; return false;
} }

View file

@ -121,18 +121,22 @@ class SerraBestiaryRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl
@Override @Override
public boolean applies(GameEvent event, Ability source, Game game) { public boolean applies(GameEvent event, Ability source, Game game) {
Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (enchantment == null) {
return false;
}
Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo());
if (enchantment != null && enchantment.getAttachedTo() != null) { if (enchantedCreature == null) {
MageObject object = game.getObject(event.getSourceId()); return false;
Optional<Ability> ability = game.getAbility(event.getTargetId(), event.getSourceId()); }
if (ability.isPresent() MageObject object = game.getObject(event.getSourceId());
&& object != null Optional<Ability> ability = game.getAbility(event.getTargetId(), event.getSourceId());
&& object.isCreature() if (ability.isPresent()
&& object.getId().equals(enchantedCreature.getId()) && object != null
&& game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId())) { && object.isCreature()
if (ability.get().getCosts().stream().anyMatch((cost) -> (cost instanceof TapSourceCost))) { && object.getId().equals(enchantedCreature.getId())
return true; && game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId())) {
} if (ability.get().getCosts().stream().anyMatch((cost) -> (cost instanceof TapSourceCost))) {
return true;
} }
} }
return false; return false;

View file

@ -127,7 +127,7 @@ class ShadesBreathSetSubtypeEffect extends ContinuousEffectImpl {
for (Permanent permanent : permanents) { for (Permanent permanent : permanents) {
if (permanent != null) { if (permanent != null) {
SubTypeList subtype = permanent.getSubtype(game); SubTypeList subtype = permanent.getSubtype(game);
if (subtype != null && subtype.size() != 1 || !subtype.contains(SubType.SHADE)) { if (subtype != null && (subtype.size() != 1 || !subtype.contains(SubType.SHADE))) {
subtype.removeAll(SubType.getCreatureTypes(false)); subtype.removeAll(SubType.getCreatureTypes(false));
subtype.add(SubType.SHADE); subtype.add(SubType.SHADE);
} }

View file

@ -56,10 +56,9 @@ import mage.target.common.TargetCreaturePermanent;
public final class SlowMotion extends CardImpl { public final class SlowMotion extends CardImpl {
public SlowMotion(UUID ownerId, CardSetInfo setInfo) { public SlowMotion(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
this.subtype.add(SubType.AURA); this.subtype.add(SubType.AURA);
// Enchant creature // Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent(); TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addTarget(auraTarget);
@ -68,7 +67,7 @@ public final class SlowMotion extends CardImpl {
this.addAbility(ability); this.addAbility(ability);
// At the beginning of the upkeep of enchanted creature's controller, that player sacrifices that creature unless he or she pays {2}. // At the beginning of the upkeep of enchanted creature's controller, that player sacrifices that creature unless he or she pays {2}.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeEquipedUnlessPaysEffect(new GenericManaCost(2)), TargetController.CONTROLLER_ATTACHED_TO, false )); this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeEquipedUnlessPaysEffect(new GenericManaCost(2)), TargetController.CONTROLLER_ATTACHED_TO, false));
// When Slow Motion is put into a graveyard from the battlefield, return Slow Motion to its owner's hand. // When Slow Motion is put into a graveyard from the battlefield, return Slow Motion to its owner's hand.
this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new ReturnToHandSourceEffect())); this.addAbility(new PutIntoGraveFromBattlefieldSourceTriggeredAbility(new ReturnToHandSourceEffect()));
@ -85,13 +84,14 @@ public final class SlowMotion extends CardImpl {
} }
class SacrificeEquipedUnlessPaysEffect extends OneShotEffect { class SacrificeEquipedUnlessPaysEffect extends OneShotEffect {
protected Cost cost; protected Cost cost;
public SacrificeEquipedUnlessPaysEffect(Cost cost) { public SacrificeEquipedUnlessPaysEffect(Cost cost) {
super(Outcome.Sacrifice); super(Outcome.Sacrifice);
this.cost = cost; this.cost = cost;
staticText = "that player sacrifices that creature unless he or she pays {2}"; staticText = "that player sacrifices that creature unless he or she pays {2}";
} }
public SacrificeEquipedUnlessPaysEffect(final SacrificeEquipedUnlessPaysEffect effect) { public SacrificeEquipedUnlessPaysEffect(final SacrificeEquipedUnlessPaysEffect effect) {
super(effect); super(effect);
@ -101,25 +101,29 @@ class SacrificeEquipedUnlessPaysEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent equipment = game.getPermanent(source.getSourceId()); Permanent equipment = game.getPermanent(source.getSourceId());
if (equipment != null && equipment.getAttachedTo() != null) { if (equipment == null) {
Permanent equipped = game.getPermanent(equipment.getAttachedTo()); return false;
Player player = game.getPlayer(equipped.getControllerId()); }
if (player != null && equipped != null) { Permanent equipped = game.getPermanent(equipment.getAttachedTo());
if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "? (Or " + equipped.getName() + " will be sacrificed.)", source, game)) { if (equipped == null) {
cost.clearPaid(); return false;
if (cost.pay(source, game, source.getSourceId(), equipped.getControllerId(), false, null)) { }
return true; Player player = game.getPlayer(equipped.getControllerId());
} if (player == null) {
} return false;
equipped.sacrifice(source.getSourceId(), game); }
if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "? (Or " + equipped.getName() + " will be sacrificed.)", source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), equipped.getControllerId(), false, null)) {
return true; return true;
} }
} }
return false; equipped.sacrifice(source.getSourceId(), game);
return true;
} }
@Override @Override
public SacrificeEquipedUnlessPaysEffect copy() { public SacrificeEquipedUnlessPaysEffect copy() {
return new SacrificeEquipedUnlessPaysEffect(this); return new SacrificeEquipedUnlessPaysEffect(this);
} }
} }

View file

@ -114,10 +114,8 @@ class SpikeCannibalEffect extends OneShotEffect {
} }
if (countersRemoved > 0) { if (countersRemoved > 0) {
if (sourcePermanent != null) { sourcePermanent.addCounters(CounterType.P1P1.createInstance(countersRemoved), source, game);
sourcePermanent.addCounters(CounterType.P1P1.createInstance(countersRemoved), source, game); return true;
return true;
}
} }
} }

View file

@ -55,7 +55,7 @@ import mage.target.common.TargetCreaturePermanent;
public final class SpyNetwork extends CardImpl { public final class SpyNetwork extends CardImpl {
public SpyNetwork(UUID ownerId, CardSetInfo setInfo) { public SpyNetwork(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
// Look at target player's hand, the top card of that player's library, and any face-down creatures he or she controls. Look at the top four cards of your library, then put them back in any order. // Look at target player's hand, the top card of that player's library, and any face-down creatures he or she controls. Look at the top four cards of your library, then put them back in any order.
this.getSpellAbility().addEffect(new SpyNetworkLookAtTargetPlayerHandEffect()); this.getSpellAbility().addEffect(new SpyNetworkLookAtTargetPlayerHandEffect());
@ -133,7 +133,7 @@ class SpyNetworkFaceDownEffect extends OneShotEffect {
filter.add(new ControllerIdPredicate(player.getId())); filter.add(new ControllerIdPredicate(player.getId()));
TargetCreaturePermanent target = new TargetCreaturePermanent(1, 1, filter, true); TargetCreaturePermanent target = new TargetCreaturePermanent(1, 1, filter, true);
if (target.canChoose(source.getSourceId(), controller.getId(), game)) { if (target.canChoose(source.getSourceId(), controller.getId(), game)) {
while (player != null && controller.chooseUse(outcome, "Look at a face down creature controlled by " + player.getLogName() + "?", source, game)) { while (controller.chooseUse(outcome, "Look at a face down creature controlled by " + player.getLogName() + "?", source, game)) {
target.clearChosen(); target.clearChosen();
while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) { while (!target.isChosen() && target.canChoose(controller.getId(), game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game); controller.chooseTarget(outcome, target, source, game);
@ -145,9 +145,7 @@ class SpyNetworkFaceDownEffect extends OneShotEffect {
Cards cards = new CardsImpl(); Cards cards = new CardsImpl();
cards.add(copyFaceDown); cards.add(copyFaceDown);
controller.lookAtCards("face down card - " + mageObject.getName(), cards, game); controller.lookAtCards("face down card - " + mageObject.getName(), cards, game);
if (player != null) { game.informPlayers(controller.getLogName() + " looks at a face down creature controlled by " + player.getLogName());
game.informPlayers(controller.getLogName() + " looks at a face down creature controlled by " + player.getLogName());
}
} }
} }
} }

View file

@ -112,8 +112,8 @@ class SummonThePackEffect extends OneShotEffect {
StringBuilder message = new StringBuilder(controller.getLogName()).append(" opened: "); StringBuilder message = new StringBuilder(controller.getLogName()).append(" opened: ");
for (Card c : boosterPack) { for (Card c : boosterPack) {
message.append(c.getName()).append(" ");
if (c != null && c.isCreature()) { if (c != null && c.isCreature()) {
message.append(c.getName()).append(" ");
message.append(" (creature card) "); message.append(" (creature card) ");
ContinuousEffect effect2 = new BecomesBlackZombieAdditionEffect(false); ContinuousEffect effect2 = new BecomesBlackZombieAdditionEffect(false);
effect2.setTargetPointer(new FixedTarget(c.getId())); effect2.setTargetPointer(new FixedTarget(c.getId()));
@ -124,7 +124,7 @@ class SummonThePackEffect extends OneShotEffect {
} }
if (creatureCards.size() > 0) { if (creatureCards.size() > 0) {
Set<Card> ccs = new HashSet<Card>(creatureCards); Set<Card> ccs = new HashSet<>(creatureCards);
game.loadCards(ccs, controller.getId()); game.loadCards(ccs, controller.getId());
controller.moveCards(ccs, Zone.BATTLEFIELD, source, game); controller.moveCards(ccs, Zone.BATTLEFIELD, source, game);
} }

View file

@ -50,7 +50,7 @@ import mage.target.common.TargetOpponent;
public final class TheBattleOfYavin extends CardImpl { public final class TheBattleOfYavin extends CardImpl {
public TheBattleOfYavin(UUID ownerId, CardSetInfo setInfo) { public TheBattleOfYavin(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{B}{B}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{B}{B}");
// For each nonland permanent target opponent controls, that player sacrificies it unless he or she pays X life. // For each nonland permanent target opponent controls, that player sacrificies it unless he or she pays X life.
this.getSpellAbility().addEffect(new TheBattleOfYavinEffect()); this.getSpellAbility().addEffect(new TheBattleOfYavinEffect());
@ -102,7 +102,7 @@ class TheBattleOfYavinEffect extends OneShotEffect {
int playerLife = opponent.getLife(); int playerLife = opponent.getLife();
for (Permanent permanent : permanents) { for (Permanent permanent : permanents) {
String message = "Pay " + amount + " life? If you don't, " + permanent.getName() + " will be sacrificed."; String message = "Pay " + amount + " life? If you don't, " + permanent.getName() + " will be sacrificed.";
if (playerLife - amount - lifePaid >= 0 && opponent != null && opponent.chooseUse(Outcome.Neutral, message, source, game)) { if (playerLife - amount - lifePaid >= 0 && opponent.chooseUse(Outcome.Neutral, message, source, game)) {
game.informPlayers(opponent.getLogName() + " pays " + amount + " life. He will not sacrifice " + permanent.getName()); game.informPlayers(opponent.getLogName() + " pays " + amount + " life. He will not sacrifice " + permanent.getName());
lifePaid += amount; lifePaid += amount;
} else { } else {

View file

@ -62,8 +62,8 @@ import mage.target.targetpointer.FixedTarget;
public final class TidalFlats extends CardImpl { public final class TidalFlats extends CardImpl {
public TidalFlats(UUID ownerId, CardSetInfo setInfo) { public TidalFlats(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{U}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}");
// {U}{U}: For each attacking creature without flying, its controller may pay {1}. If he or she doesn't, creatures you control blocking that creature gain first strike until end of turn. // {U}{U}: For each attacking creature without flying, its controller may pay {1}. If he or she doesn't, creatures you control blocking that creature gain first strike until end of turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TidalFlatsEffect(), new ManaCostsImpl("{U}{U}"))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TidalFlatsEffect(), new ManaCostsImpl("{U}{U}")));
} }
@ -79,8 +79,9 @@ public final class TidalFlats extends CardImpl {
} }
class TidalFlatsEffect extends OneShotEffect { class TidalFlatsEffect extends OneShotEffect {
private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creature without flying"); private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creature without flying");
static { static {
filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class)));
} }
@ -103,43 +104,45 @@ class TidalFlatsEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
game.getPlayerList(); game.getPlayerList();
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null) { if (controller == null) {
Player player = game.getPlayer(game.getActivePlayerId()); return false;
Cost cost = new ManaCostsImpl("{1}"); }
List<Permanent> affectedPermanents = new ArrayList<>(); Player player = game.getPlayer(game.getActivePlayerId());
for (Permanent permanent : game.getState().getBattlefield().getAllActivePermanents(filter, player.getId(), game)) { if (player == null) {
cost.clearPaid(); return false;
String message = "Pay " + cost.getText() + " for " + permanent.getLogName() + "? If you don't, creatures " + controller.getLogName() + " controls blocking it gain first strike until end of turn."; }
if (player != null && player.chooseUse(Outcome.Benefit, message, source, game)) { Cost cost = new ManaCostsImpl("{1}");
if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) { List<Permanent> affectedPermanents = new ArrayList<>();
game.informPlayers(player.getLogName() + " paid " + cost.getText() + " for " + permanent.getLogName()); for (Permanent permanent : game.getState().getBattlefield().getAllActivePermanents(filter, player.getId(), game)) {
continue; cost.clearPaid();
} else { String message = "Pay " + cost.getText() + " for " + permanent.getLogName() + "? If you don't, creatures " + controller.getLogName() + " controls blocking it gain first strike until end of turn.";
game.informPlayers(player.getLogName() + " didn't pay " + cost.getText() + " for " + permanent.getLogName()); if (player.chooseUse(Outcome.Benefit, message, source, game)) {
affectedPermanents.add(permanent); if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
} game.informPlayers(player.getLogName() + " paid " + cost.getText() + " for " + permanent.getLogName());
} else { } else {
game.informPlayers(player.getLogName() + " didn't pay " + cost.getText() + " for " + permanent.getLogName()); game.informPlayers(player.getLogName() + " didn't pay " + cost.getText() + " for " + permanent.getLogName());
affectedPermanents.add(permanent); affectedPermanents.add(permanent);
} }
} else {
game.informPlayers(player.getLogName() + " didn't pay " + cost.getText() + " for " + permanent.getLogName());
affectedPermanents.add(permanent);
} }
}
for (Permanent permanent : affectedPermanents) { for (Permanent permanent : affectedPermanents) {
CombatGroup group = game.getCombat().findGroup(permanent.getId()); CombatGroup group = game.getCombat().findGroup(permanent.getId());
if (group != null) { if (group != null) {
for (UUID blockerId : group.getBlockers()) { for (UUID blockerId : group.getBlockers()) {
Permanent blocker = game.getPermanent(blockerId); Permanent blocker = game.getPermanent(blockerId);
if (blocker != null && Objects.equals(blocker.getControllerId(), controller.getId())) { if (blocker != null && Objects.equals(blocker.getControllerId(), controller.getId())) {
ContinuousEffect effect = new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn); ContinuousEffect effect = new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn);
effect.setTargetPointer(new FixedTarget(blocker.getId())); effect.setTargetPointer(new FixedTarget(blocker.getId()));
game.addEffect(effect, source); game.addEffect(effect, source);
}
} }
} }
} }
return true;
} }
return false; return true;
} }
} }

View file

@ -122,7 +122,7 @@ class TorrentialGearhulkEffect extends OneShotEffect {
game.addEffect(effect, source); game.addEffect(effect, source);
} }
} }
} else { } else if (card != null) {
Logger.getLogger(TorrentialGearhulkEffect.class).error("Torrential Gearhulk - Instant card without spellAbility : " + card.getName()); Logger.getLogger(TorrentialGearhulkEffect.class).error("Torrential Gearhulk - Instant card without spellAbility : " + card.getName());
return false; return false;
} }

View file

@ -55,7 +55,7 @@ import mage.target.common.TargetCreaturePermanent;
public final class TreacherousLink extends CardImpl { public final class TreacherousLink extends CardImpl {
public TreacherousLink(UUID ownerId, CardSetInfo setInfo) { public TreacherousLink(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{B}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
this.subtype.add(SubType.AURA); this.subtype.add(SubType.AURA);
// Enchant creature // Enchant creature
@ -109,12 +109,15 @@ class TreacherousLinkEffect extends ReplacementEffectImpl {
public boolean replaceEvent(GameEvent event, Ability source, Game game) { public boolean replaceEvent(GameEvent event, Ability source, Game game) {
DamageEvent damageEvent = (DamageEvent) event; DamageEvent damageEvent = (DamageEvent) event;
Permanent enchantedCreature = game.getPermanentOrLKIBattlefield(damageEvent.getTargetId()); Permanent enchantedCreature = game.getPermanentOrLKIBattlefield(damageEvent.getTargetId());
Player controller = game.getPlayer(enchantedCreature.getControllerId()); if (enchantedCreature == null) {
if (enchantedCreature != null && controller != null) { return false;
controller.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects());
return true;
} }
return false; Player controller = game.getPlayer(enchantedCreature.getControllerId());
if (controller == null) {
return false;
}
controller.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects());
return true;
} }
@Override @Override

View file

@ -55,7 +55,7 @@ public final class WallOfPutridFlesh extends CardImpl {
public WallOfPutridFlesh(UUID ownerId, CardSetInfo setInfo) { public WallOfPutridFlesh(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
this.subtype.add(SubType.WALL); this.subtype.add(SubType.WALL);
this.power = new MageInt(2); this.power = new MageInt(2);
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
@ -83,7 +83,7 @@ public final class WallOfPutridFlesh extends CardImpl {
class PreventDamageToSourceByEnchantedCreatures extends PreventAllDamageToSourceEffect { class PreventDamageToSourceByEnchantedCreatures extends PreventAllDamageToSourceEffect {
public PreventDamageToSourceByEnchantedCreatures(){ public PreventDamageToSourceByEnchantedCreatures() {
super(Duration.WhileOnBattlefield); super(Duration.WhileOnBattlefield);
} }
@ -100,10 +100,10 @@ class PreventDamageToSourceByEnchantedCreatures extends PreventAllDamageToSource
} }
public boolean isEnchantedCreature(MageObject input, Game game) { public boolean isEnchantedCreature(MageObject input, Game game) {
if (input != null && !input.isCreature()) { if (input == null || input.isCreature()) {
return false; return false;
} }
for (UUID attachmentId : ((Permanent)input).getAttachments()) { for (UUID attachmentId : ((Permanent) input).getAttachments()) {
Permanent attachment = game.getPermanent(attachmentId); Permanent attachment = game.getPermanent(attachmentId);
if (attachment != null && attachment.isEnchantment()) { if (attachment != null && attachment.isEnchantment()) {
return true; return true;

View file

@ -47,7 +47,7 @@ import mage.players.Player;
public final class WheelOfTorture extends CardImpl { public final class WheelOfTorture extends CardImpl {
public WheelOfTorture(UUID ownerId, CardSetInfo setInfo) { public WheelOfTorture(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// At the beginning of each opponent's upkeep, Wheel of Torture deals X damage to that player, where X is 3 minus the number of cards in their hand. // At the beginning of each opponent's upkeep, Wheel of Torture deals X damage to that player, where X is 3 minus the number of cards in their hand.
Ability ability = new BeginningOfUpkeepTriggeredAbility(new WheelOfTortureEffect(), TargetController.OPPONENT, false); Ability ability = new BeginningOfUpkeepTriggeredAbility(new WheelOfTortureEffect(), TargetController.OPPONENT, false);
@ -64,7 +64,6 @@ public final class WheelOfTorture extends CardImpl {
} }
} }
class WheelOfTortureEffect extends OneShotEffect { class WheelOfTortureEffect extends OneShotEffect {
private WheelOfTortureEffect(final WheelOfTortureEffect effect) { private WheelOfTortureEffect(final WheelOfTortureEffect effect) {
@ -78,16 +77,11 @@ class WheelOfTortureEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(targetPointer.getFirst(game, source)); Player player = game.getPlayer(targetPointer.getFirst(game, source));
if(player != null) if (player != null) {
{
int amount = 3 - player.getHand().size(); int amount = 3 - player.getHand().size();
if(amount > 0) if (amount > 0) {
{ player.damage(amount, source.getSourceId(), game, false, true);
if (player != null) { return true;
player.damage(amount, source.getSourceId(), game, false, true);
return true;
}
} }
} }
return false; return false;
@ -103,5 +97,4 @@ class WheelOfTortureEffect extends OneShotEffect {
return "Wheel of Torture deals X damage to that player, where X is 3 minus the number of cards in their hand"; return "Wheel of Torture deals X damage to that player, where X is 3 minus the number of cards in their hand";
} }
} }

View file

@ -83,6 +83,9 @@ class WretchedBanquetEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent targetCreature = game.getPermanent(source.getFirstTarget()); Permanent targetCreature = game.getPermanent(source.getFirstTarget());
if (targetCreature == null) {
return false;
}
List<Permanent> creatures = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game); List<Permanent> creatures = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game);
int minPower = targetCreature.getPower().getValue() + 1; int minPower = targetCreature.getPower().getValue() + 1;
@ -92,7 +95,7 @@ class WretchedBanquetEffect extends OneShotEffect {
} }
} }
if (targetCreature != null && targetCreature.getPower().getValue() <= minPower) { if (targetCreature.getPower().getValue() <= minPower) {
targetCreature.destroy(source.getSourceId(), game, false); targetCreature.destroy(source.getSourceId(), game, false);
return true; return true;
} }

View file

@ -1,6 +1,5 @@
package mage.verify; package mage.verify;
import javassist.bytecode.SignatureAttribute;
import mage.ObjectColor; import mage.ObjectColor;
import mage.cards.*; import mage.cards.*;
import mage.cards.basiclands.BasicLand; import mage.cards.basiclands.BasicLand;
@ -352,9 +351,7 @@ public class VerifyCardDataTest {
//Assert.assertNotNull("Can't create token by default constructor", token); //Assert.assertNotNull("Can't create token by default constructor", token);
if (token == null) { if (token == null) {
Assert.fail("Can't create token by default constructor: " + className); Assert.fail("Can't create token by default constructor: " + className);
} } else if (tokDataNamesIndex.getOrDefault(token.getName(), "").isEmpty()) {
if (tokDataNamesIndex.getOrDefault(token.getName(), "").isEmpty()) {
errorsList.add("error, can't find data in card-pictures-tok.txt for token: " + tokenClass.getName() + " -> " + token.getName()); errorsList.add("error, can't find data in card-pictures-tok.txt for token: " + tokenClass.getName() + " -> " + token.getName());
} }
} }

View file

@ -103,7 +103,7 @@ public class DoUnlessTargetPlayerOrTargetsControllerPaysEffect extends OneShotEf
} }
if (targetController != null) { if (targetController != null) {
MageObject sourceObject = game.getObject(source.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId());
if (targetController != null && sourceObject != null) { if (sourceObject != null) {
Cost costToPay; Cost costToPay;
if (cost != null) { if (cost != null) {
costToPay = cost.copy(); costToPay = cost.copy();
@ -120,7 +120,7 @@ public class DoUnlessTargetPlayerOrTargetsControllerPaysEffect extends OneShotEf
message = CardUtil.replaceSourceName(message, sourceObject.getName()); message = CardUtil.replaceSourceName(message, sourceObject.getName());
boolean result = true; boolean result = true;
boolean doEffect = true; boolean doEffect = true;
// check if targetController is willing to pay // check if targetController is willing to pay
if (costToPay.canPay(source, source.getSourceId(), targetController.getId(), game) && targetController.chooseUse(Outcome.Detriment, message, source, game)) { if (costToPay.canPay(source, source.getSourceId(), targetController.getId(), game) && targetController.chooseUse(Outcome.Detriment, message, source, game)) {
costToPay.clearPaid(); costToPay.clearPaid();
@ -128,10 +128,10 @@ public class DoUnlessTargetPlayerOrTargetsControllerPaysEffect extends OneShotEf
if (!game.isSimulation()) { if (!game.isSimulation()) {
game.informPlayers(targetController.getLogName() + " pays the cost to prevent the effect"); game.informPlayers(targetController.getLogName() + " pays the cost to prevent the effect");
} }
doEffect = false; doEffect = false;
} }
} }
// do the effects if not paid // do the effects if not paid
if (doEffect) { if (doEffect) {
for (Effect effect : executingEffects) { for (Effect effect : executingEffects) {