Fixed that some AlternativeCostSourceAbilities had no sourceId set.

This commit is contained in:
LevelX2 2020-09-17 10:00:46 +02:00
parent 0a66f1fca0
commit 4050631807
9 changed files with 113 additions and 37 deletions

View file

@ -70,7 +70,7 @@ class AlurenRuleEffect extends ContinuousEffectImpl {
filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, 4));
}
private static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(null, SourceIsSpellCondition.instance, null, filter, true);
private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(null, SourceIsSpellCondition.instance, null, filter, true);
public AlurenRuleEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
@ -86,6 +86,12 @@ class AlurenRuleEffect extends ContinuousEffectImpl {
return new AlurenRuleEffect(this);
}
@Override
public void init(Ability source, Game game, UUID activePlayerId) {
super.init(source, game, activePlayerId);
alternativeCastingCostAbility.setSourceId(source.getSourceId());
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());

View file

@ -83,17 +83,14 @@ class SpellWithManaCostLessThanOrEqualToCondition implements Condition {
*/
class AsForetoldAlternativeCost extends AlternativeCostSourceAbility {
private UUID sourceAsForetold;
private boolean wasActivated;
AsForetoldAlternativeCost(UUID sourceAsForetold, int timeCounters) {
AsForetoldAlternativeCost(int timeCounters) {
super(new ManaCostsImpl("{0}"), new SpellWithManaCostLessThanOrEqualToCondition(timeCounters));
this.sourceAsForetold = sourceAsForetold;
}
private AsForetoldAlternativeCost(final AsForetoldAlternativeCost ability) {
super(ability);
this.sourceAsForetold = ability.sourceAsForetold;
this.wasActivated = ability.wasActivated;
}
@ -105,7 +102,7 @@ class AsForetoldAlternativeCost extends AlternativeCostSourceAbility {
@Override
public boolean askToActivateAlternativeCosts(Ability ability, Game game) {
Player controller = game.getPlayer(ability.getControllerId());
Permanent asForetold = game.getPermanent(sourceAsForetold);
Permanent asForetold = game.getPermanent(getSourceId());
if (controller != null
&& asForetold != null) {
if (controller.chooseUse(Outcome.Neutral, "Do you wish to use "
@ -156,8 +153,9 @@ class AsForetoldAddAltCostEffect extends ContinuousEffectImpl {
// If we haven't used it yet this turn, give the option of using the zero alternative cost
if (wasItUsed == null) {
int timeCounters = sourcePermanent.getCounters(game).getCount("time");
controller.getAlternativeSourceCosts().add(
new AsForetoldAlternativeCost(sourcePermanent.getId(), timeCounters));
AsForetoldAlternativeCost alternateCostAbility = new AsForetoldAlternativeCost(timeCounters);
alternateCostAbility.setSourceId(source.getSourceId());
controller.getAlternativeSourceCosts().add(alternateCostAbility);
}
// Return true even if we didn't add the alt cost. We still applied the effect
return true;

View file

@ -49,7 +49,7 @@ class DreamHallsEffect extends ContinuousEffectImpl {
filter.add(new SharesColorWithSourcePredicate());
}
static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new DiscardCardCost(filter), SourceIsSpellCondition.instance);
private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new DiscardCardCost(filter), SourceIsSpellCondition.instance);
public DreamHallsEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
@ -64,7 +64,13 @@ class DreamHallsEffect extends ContinuousEffectImpl {
public DreamHallsEffect copy() {
return new DreamHallsEffect(this);
}
@Override
public void init(Ability source, Game game, UUID activePlayerId) {
super.init(source, game, activePlayerId);
alternativeCastingCostAbility.setSourceId(source.getSourceId());
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());

View file

@ -60,7 +60,7 @@ class KentaroTheSmilingCatCastingEffect extends ContinuousEffectImpl {
filterSamurai.add(SubType.SAMURAI.getPredicate());
}
static final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(
private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(
SourceIsSpellCondition.instance, null, filterSamurai, true, new ColorlessConvertedManaCost());
public KentaroTheSmilingCatCastingEffect() {
@ -77,6 +77,12 @@ class KentaroTheSmilingCatCastingEffect extends ContinuousEffectImpl {
return new KentaroTheSmilingCatCastingEffect(this);
}
@Override
public void init(Ability source, Game game, UUID activePlayerId) {
super.init(source, game, activePlayerId);
alternativeCastingCostAbility.setSourceId(source.getSourceId());
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());

View file

@ -48,7 +48,7 @@ class RooftopStormRuleEffect extends ContinuousEffectImpl {
filter.add(CardType.CREATURE.getPredicate());
}
static AlternativeCostSourceAbility alternativeCastingCostAbility
private final AlternativeCostSourceAbility alternativeCastingCostAbility
= new AlternativeCostSourceAbility(new ManaCostsImpl("{0}"), SourceIsSpellCondition.instance, null, filter, true);
public RooftopStormRuleEffect() {
@ -65,6 +65,12 @@ class RooftopStormRuleEffect extends ContinuousEffectImpl {
return new RooftopStormRuleEffect(this);
}
@Override
public void init(Ability source, Game game, UUID activePlayerId) {
super.init(source, game, activePlayerId);
alternativeCastingCostAbility.setSourceId(source.getSourceId());
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());

View file

@ -192,16 +192,15 @@ public class CastFromHandWithoutPayingManaCostTest extends CardTestPlayerBase {
/**
* Omniscience is not allowing me to cast spells for free. I'm playing a
* Commander game against the Computer, if that helps.
* <p>
*
* Edit: It's not letting me cast fused spells for free. Others seems to be
* working.
*/
@Test
@Ignore // targeting of fused/split spells not supported by testplayer
public void testCastingFusedSpell() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Omniscience");
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
@ -214,13 +213,18 @@ public class CastFromHandWithoutPayingManaCostTest extends CardTestPlayerBase {
*/
addCard(Zone.HAND, playerA, "Far // Away");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Far // Away", "Silvercoat Lion^targetPlayer=PlayerB");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Far // Away");
setChoice(playerA, "Yes"); // Cast without paying its mana cost?
addTarget(playerA, "Silvercoat Lion");
addTarget(playerA, playerB);
playerB.addTarget("Pillarfield Ox");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertHandCount(playerA, 1);
assertAllCommandsUsed();
assertHandCount(playerA, "Silvercoat Lion", 1);
assertHandCount(playerB, 0);
assertGraveyardCount(playerA, "Far // Away", 1);
@ -228,6 +232,7 @@ public class CastFromHandWithoutPayingManaCostTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "Pillarfield Ox", 0);
assertGraveyardCount(playerB, "Pillarfield Ox", 1);
}
/**
* If another effect (e.g. Future Sight) allows you to cast nonland cards
@ -335,6 +340,47 @@ public class CastFromHandWithoutPayingManaCostTest extends CardTestPlayerBase {
assertLife(playerB, 20);
}
// Not sure what the exact interaction is, but when Omniscience is on the field with Jodah,
// if you say "no" to the Jodah cast option to get to the Omniscience option, then the game will initiate a rollback.
@Test
public void test_OmniscienceAndJodah() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
// Flying
// You may pay WUBRG rather than pay the mana cost for spells that you cast.
addCard(Zone.BATTLEFIELD, playerA, "Jodah, Archmage Eternal"); // Creature {1}{U}{R}{W} (4/3)
// You may cast nonland cards from your hand without paying their mana costs.
addCard(Zone.HAND, playerA, "Omniscience"); // Enchantment {7}{U}{U}{U}
// Creature - 3/3 Swampwalk
addCard(Zone.HAND, playerA, "Bog Wraith", 1); // Creature {3}{B} (3/3)
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Omniscience");
setChoice(playerA, "Yes"); // Pay alternative costs? ({W}{U}{B}{R}{G})
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bog Wraith");
// The order of the two alternate casting abilities is not fixed, so it's not clear which ability is asked for first
setChoice(playerA, "No"); // Pay alternative costs? ({W}{U}{B}{R}{G})
setChoice(playerA, "Yes"); // Cast without paying its mana cost?
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertAllCommandsUsed();
assertPermanentCount(playerA, "Omniscience", 1);
assertPermanentCount(playerA, "Bog Wraith", 1);
}
@Test
public void testJelevaCastingSavageBeatingFromExile() {

View file

@ -16,6 +16,7 @@ import mage.players.Player;
import mage.util.CardUtil;
import java.util.Iterator;
import mage.MageObject;
/**
* @author LevelX2
@ -135,7 +136,6 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
} else {
costChoiceText = alternativeCostsToCheck.isEmpty() ? "Cast without paying its mana cost?" : "Pay alternative costs? (" + alternativeCostsToCheck.getText() + ')';
}
if (alternativeCostsToCheck.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
&& player.chooseUse(Outcome.Benefit, costChoiceText, this, game)) {
if (ability instanceof SpellAbility) {

View file

@ -20,9 +20,8 @@ import java.util.UUID;
public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImpl {
private final FilterCard filter;
private final boolean fromHand;
private final AlternativeCostSourceAbility alternativeCastingCostAbility;
public CastFromHandWithoutPayingManaCostEffect() {
this(StaticFilters.FILTER_CARDS_NON_LAND, true);
}
@ -33,8 +32,13 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp
public CastFromHandWithoutPayingManaCostEffect(FilterCard filter, boolean fromHand, Duration duration) {
super(duration, Outcome.Detriment);
this.filter = filter;
this.fromHand = fromHand;
Condition condition;
if (fromHand) {
condition = new CompoundCondition(SourceIsSpellCondition.instance, IsBeingCastFromHandCondition.instance);
} else {
condition = SourceIsSpellCondition.instance;
}
this.alternativeCastingCostAbility = new AlternativeCostSourceAbility(null, condition, null, filter, true);
this.staticText = "You may cast " + filter.getMessage()
+ (fromHand ? " from your hand" : "")
+ " without paying their mana costs";
@ -42,8 +46,7 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp
private CastFromHandWithoutPayingManaCostEffect(final CastFromHandWithoutPayingManaCostEffect effect) {
super(effect);
this.filter = effect.filter;
this.fromHand = effect.fromHand;
this.alternativeCastingCostAbility = effect.alternativeCastingCostAbility;
}
@Override
@ -51,21 +54,19 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp
return new CastFromHandWithoutPayingManaCostEffect(this);
}
@Override
public void init(Ability source, Game game, UUID activePlayerId) {
super.init(source, game, activePlayerId);
alternativeCastingCostAbility.setSourceId(source.getSourceId());
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
Condition condition;
if (fromHand) {
condition = new CompoundCondition(SourceIsSpellCondition.instance, IsBeingCastFromHandCondition.instance);
} else {
condition = SourceIsSpellCondition.instance;
}
controller.getAlternativeSourceCosts().add(new AlternativeCostSourceAbility(
null, condition, null, filter, true
));
controller.getAlternativeSourceCosts().add(alternativeCastingCostAbility);
return true;
}
@ -92,7 +93,7 @@ enum IsBeingCastFromHandCondition implements Condition {
}
if (object instanceof Spell) { // needed to check if it can be cast by alternate cost
Spell spell = (Spell) object;
return spell.getFromZone() == Zone.HAND;
return Zone.HAND.equals(spell.getFromZone());
}
if (object instanceof Card) { // needed for the check what's playable
Card card = (Card) object;

View file

@ -1,6 +1,7 @@
package mage.abilities.effects.common.continuous;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.common.SourceIsSpellCondition;
import mage.abilities.costs.AlternativeCostSourceAbility;
@ -19,7 +20,7 @@ import mage.players.Player;
*/
public class WUBRGInsteadEffect extends ContinuousEffectImpl {
static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new ManaCostsImpl("{W}{U}{B}{R}{G}"), SourceIsSpellCondition.instance);
private final AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new ManaCostsImpl("{W}{U}{B}{R}{G}"), SourceIsSpellCondition.instance);
public WUBRGInsteadEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
@ -35,6 +36,12 @@ public class WUBRGInsteadEffect extends ContinuousEffectImpl {
return new WUBRGInsteadEffect(this);
}
@Override
public void init(Ability source, Game game, UUID activePlayerId) {
super.init(source, game, activePlayerId);
alternativeCastingCostAbility.setSourceId(source.getSourceId());
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());