mirror of
https://github.com/correl/mage.git
synced 2024-12-24 11:50:45 +00:00
* Added handling of triggered mana to available mana calculation (fixes #585).
This commit is contained in:
parent
5be6e9398a
commit
89249888b5
26 changed files with 544 additions and 96 deletions
|
@ -20,7 +20,6 @@ import mage.game.events.GameEvent;
|
|||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.ManaEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -53,7 +53,7 @@ class DesolationEffect extends OneShotEffect {
|
|||
|
||||
public DesolationEffect() {
|
||||
super(Outcome.Damage);
|
||||
this.staticText = "each player who tapped a land for mana this turn sacrifices a land. Desolation deals 2 damage to each player who sacrificed a Plains this way";
|
||||
this.staticText = "each player who tapped a land for mana this turn sacrifices a land. {this} deals 2 damage to each player who sacrificed a Plains this way";
|
||||
}
|
||||
|
||||
public DesolationEffect(DesolationEffect copy) {
|
||||
|
@ -108,7 +108,8 @@ class DesolationWatcher extends Watcher {
|
|||
if (event.getType() == GameEvent.EventType.UNTAP_STEP_PRE) {
|
||||
reset();
|
||||
}
|
||||
if (event.getType() == GameEvent.EventType.TAPPED_FOR_MANA) {
|
||||
if (event.getType() == GameEvent.EventType.TAPPED_FOR_MANA
|
||||
&& !game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA
|
||||
UUID playerId = event.getPlayerId();
|
||||
if (playerId != null) {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
|
|
|
@ -86,7 +86,7 @@ class ForbiddenOrchardTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return event.getSourceId().equals(getSourceId());
|
||||
return event.getSourceId().equals(getSourceId()) && !game.inCheckPlayableState();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import mage.Mana;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -43,10 +45,10 @@ public final class GauntletOfPower extends CardImpl {
|
|||
// As Gauntlet of Power enters the battlefield, choose a color.
|
||||
this.addAbility(new EntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral)));
|
||||
// Creatures of the chosen color get +1/+1.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GauntletOfPowerEffect1()));
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GauntletOfPowerBoostEffect()));
|
||||
|
||||
// Whenever a basic land is tapped for mana of the chosen color, its controller adds one mana of that color.
|
||||
this.addAbility(new TapForManaAllTriggeredAbility(new GauntletOfPowerEffectEffect2(), filter, SetTargetPointer.PERMANENT));
|
||||
this.addAbility(new GauntletOfPowerTapForManaAllTriggeredAbility(new GauntletOfPowerManaEffect2(), filter, SetTargetPointer.PERMANENT));
|
||||
}
|
||||
|
||||
public GauntletOfPower(final GauntletOfPower card) {
|
||||
|
@ -59,22 +61,22 @@ public final class GauntletOfPower extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class GauntletOfPowerEffect1 extends ContinuousEffectImpl {
|
||||
class GauntletOfPowerBoostEffect extends ContinuousEffectImpl {
|
||||
|
||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
|
||||
|
||||
public GauntletOfPowerEffect1() {
|
||||
public GauntletOfPowerBoostEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature);
|
||||
staticText = "Creatures of the chosen color get +1/+1";
|
||||
}
|
||||
|
||||
public GauntletOfPowerEffect1(final GauntletOfPowerEffect1 effect) {
|
||||
public GauntletOfPowerBoostEffect(final GauntletOfPowerBoostEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GauntletOfPowerEffect1 copy() {
|
||||
return new GauntletOfPowerEffect1(this);
|
||||
public GauntletOfPowerBoostEffect copy() {
|
||||
return new GauntletOfPowerBoostEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -93,18 +95,18 @@ class GauntletOfPowerEffect1 extends ContinuousEffectImpl {
|
|||
|
||||
}
|
||||
|
||||
class TapForManaAllTriggeredAbility extends TriggeredManaAbility {
|
||||
class GauntletOfPowerTapForManaAllTriggeredAbility extends TriggeredManaAbility {
|
||||
|
||||
private final FilterPermanent filter;
|
||||
private final SetTargetPointer setTargetPointer;
|
||||
|
||||
public TapForManaAllTriggeredAbility(ManaEffect effect, FilterPermanent filter, SetTargetPointer setTargetPointer) {
|
||||
public GauntletOfPowerTapForManaAllTriggeredAbility(ManaEffect effect, FilterPermanent filter, SetTargetPointer setTargetPointer) {
|
||||
super(Zone.BATTLEFIELD, effect, false);
|
||||
this.filter = filter;
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
}
|
||||
|
||||
public TapForManaAllTriggeredAbility(TapForManaAllTriggeredAbility ability) {
|
||||
public GauntletOfPowerTapForManaAllTriggeredAbility(GauntletOfPowerTapForManaAllTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter.copy();
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
|
@ -156,8 +158,8 @@ class TapForManaAllTriggeredAbility extends TriggeredManaAbility {
|
|||
}
|
||||
|
||||
@Override
|
||||
public TapForManaAllTriggeredAbility copy() {
|
||||
return new TapForManaAllTriggeredAbility(this);
|
||||
public GauntletOfPowerTapForManaAllTriggeredAbility copy() {
|
||||
return new GauntletOfPowerTapForManaAllTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -167,14 +169,14 @@ class TapForManaAllTriggeredAbility extends TriggeredManaAbility {
|
|||
}
|
||||
}
|
||||
|
||||
class GauntletOfPowerEffectEffect2 extends ManaEffect {
|
||||
class GauntletOfPowerManaEffect2 extends ManaEffect {
|
||||
|
||||
public GauntletOfPowerEffectEffect2() {
|
||||
public GauntletOfPowerManaEffect2() {
|
||||
super();
|
||||
staticText = "its controller adds one additional mana of that color";
|
||||
}
|
||||
|
||||
public GauntletOfPowerEffectEffect2(final GauntletOfPowerEffectEffect2 effect) {
|
||||
public GauntletOfPowerManaEffect2(final GauntletOfPowerManaEffect2 effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
|
@ -187,6 +189,18 @@ class GauntletOfPowerEffectEffect2 extends ManaEffect {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Mana> getNetMana(Game game, Ability source) {
|
||||
List<Mana> netMana = new ArrayList<>();
|
||||
if (game != null) {
|
||||
Mana mana = (Mana) getValue("mana");
|
||||
if (mana != null) {
|
||||
netMana.add(mana.copy());
|
||||
}
|
||||
}
|
||||
return netMana;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mana produceMana(Game game, Ability source) {
|
||||
if (game != null) {
|
||||
|
@ -202,7 +216,7 @@ class GauntletOfPowerEffectEffect2 extends ManaEffect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public GauntletOfPowerEffectEffect2 copy() {
|
||||
return new GauntletOfPowerEffectEffect2(this);
|
||||
public GauntletOfPowerManaEffect2 copy() {
|
||||
return new GauntletOfPowerManaEffect2(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import mage.MageInt;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -11,8 +12,6 @@ import mage.abilities.effects.common.FlipSourceEffect;
|
|||
import mage.abilities.effects.common.ManaEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceColor;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledLandPermanent;
|
||||
|
@ -26,9 +25,10 @@ import mage.game.permanent.Permanent;
|
|||
import mage.game.permanent.token.TokenImpl;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceColor;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
@ -133,9 +133,55 @@ class SasayasEssenceManaEffect extends ManaEffect {
|
|||
|
||||
@Override
|
||||
public List<Mana> getNetMana(Game game, Ability source) {
|
||||
return new ArrayList<>();
|
||||
List<Mana> netMana = new ArrayList<>();
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Mana producedMana = (Mana) this.getValue("mana");
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (controller != null && producedMana != null && permanent != null) {
|
||||
FilterPermanent filter = new FilterLandPermanent();
|
||||
filter.add(Predicates.not(new PermanentIdPredicate(permanent.getId())));
|
||||
filter.add(new NamePredicate(permanent.getName()));
|
||||
int count = game.getBattlefield().countAll(filter, controller.getId(), game);
|
||||
if (count > 0) {
|
||||
if (producedMana.getBlack() > 0) {
|
||||
netMana.add(Mana.BlackMana(count));
|
||||
}
|
||||
if (producedMana.getRed() > 0) {
|
||||
netMana.add(Mana.RedMana(count));
|
||||
}
|
||||
if (producedMana.getBlue() > 0) {
|
||||
netMana.add(Mana.BlueMana(count));
|
||||
}
|
||||
if (producedMana.getGreen() > 0) {
|
||||
netMana.add(Mana.GreenMana(count));
|
||||
}
|
||||
if (producedMana.getWhite() > 0) {
|
||||
netMana.add(Mana.WhiteMana(count));
|
||||
}
|
||||
if (producedMana.getColorless() > 0) {
|
||||
netMana.add(Mana.ColorlessMana(count));
|
||||
}
|
||||
}
|
||||
}
|
||||
return netMana;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* RULINGS 6/1/2005 If Sasaya’s Essence’s controller has four Forests and
|
||||
* taps one of them for Green, the Essence will add GreenGreenGreen to that
|
||||
* player’s mana pool for a total of GreenGreenGreenGreen.
|
||||
*
|
||||
* 6/1/2005 If Sasaya’s Essence’s controller has four Mossfire Valley and
|
||||
* taps one of them for RedGreen, the Essence will add three mana (one for
|
||||
* each other Mossfire Valley) of any combination of Red and/or Green to
|
||||
* that player’s mana pool.
|
||||
*
|
||||
* 6/1/2005 If Sasaya’s Essence’s controller has two Brushlands and taps one
|
||||
* of them for White, Sasaya’s Essence adds another White to that player’s
|
||||
* mana pool. It won’t produce Green or Colorless unless the land was tapped
|
||||
* for Green or Colorless instead.
|
||||
*/
|
||||
@Override
|
||||
public Mana produceMana(Game game, Ability source) {
|
||||
Mana newMana = new Mana();
|
||||
|
|
|
@ -74,6 +74,9 @@ class SavageFirecatTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA
|
||||
return false;
|
||||
}
|
||||
return game.getCard(event.getSourceId()).isLand() &&
|
||||
event.getPlayerId().equals(this.controllerId);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import mage.constants.Zone;
|
|||
import mage.filter.common.FilterControlledLandPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
|
@ -72,12 +71,15 @@ class VorinclexTriggeredAbility2 extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == EventType.TAPPED_FOR_MANA;
|
||||
return event.getType() == GameEvent.EventType.TAPPED_FOR_MANA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.getOpponents(controllerId).contains(event.getPlayerId())) {
|
||||
if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA
|
||||
return false;
|
||||
}
|
||||
if (game.getOpponents(getControllerId()).contains(event.getPlayerId())) {
|
||||
Permanent permanent = game.getPermanent(event.getSourceId());
|
||||
if (permanent != null && permanent.isLand()) {
|
||||
getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getId()));
|
||||
|
|
|
@ -22,7 +22,8 @@ import mage.filter.common.FilterLandPermanent;
|
|||
public final class WintersNight extends CardImpl {
|
||||
|
||||
private static final FilterLandPermanent filter = new FilterLandPermanent("a player taps a snow land");
|
||||
{
|
||||
|
||||
static {
|
||||
filter.add(SuperType.SNOW.getPredicate());
|
||||
}
|
||||
|
||||
|
|
|
@ -29,13 +29,16 @@ public class SasayaOrochiAscendantTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerA, "Upwelling", 1); // Enchantment {3}{G}
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reveal your hand: If you have seven or more land cards in your hand, flip");
|
||||
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}");
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}");
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Upwelling");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Sasaya's Essence", 1);
|
||||
assertPermanentCount(playerA, "Upwelling", 1);
|
||||
|
||||
|
@ -45,7 +48,89 @@ public class SasayaOrochiAscendantTest extends CardTestPlayerBase {
|
|||
assertDuplicatedManaOptions(manaOptions);
|
||||
|
||||
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
|
||||
assertManaOptions("{G}", manaOptions);
|
||||
assertManaOptions("{G}{G}{G}", manaOptions);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSasayasEssence2() {
|
||||
addCard(Zone.HAND, playerA, "Plains", 7);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Brushland", 3);
|
||||
|
||||
// Reveal your hand: If you have seven or more land cards in your hand, flip Sasaya, Orochi Ascendant.
|
||||
// Sasaya's Essence: Legendary Enchantment
|
||||
// Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana of any type that land produced.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Sasaya, Orochi Ascendant", 1);
|
||||
// Mana pools don't empty as steps and phases end.
|
||||
addCard(Zone.HAND, playerA, "Upwelling", 1); // Enchantment {3}{G}
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reveal your hand: If you have seven or more land cards in your hand, flip");
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}");
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}");
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Upwelling");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Sasaya's Essence", 1);
|
||||
assertPermanentCount(playerA, "Upwelling", 1);
|
||||
|
||||
assertManaPool(playerA, ManaType.GREEN, 2);
|
||||
|
||||
assertLife(playerA, 18);
|
||||
|
||||
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
|
||||
assertDuplicatedManaOptions(manaOptions);
|
||||
|
||||
Assert.assertEquals("mana variations don't fit", 3, manaOptions.size());
|
||||
assertManaOptions("{C}{C}{C}", manaOptions);
|
||||
assertManaOptions("{G}{G}{G}", manaOptions);
|
||||
assertManaOptions("{W}{W}{W}", manaOptions);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSasayasEssence3() {
|
||||
addCard(Zone.HAND, playerA, "Plains", 7);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||
// {1}, {T}: Add {R}{G}.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mossfire Valley", 2);
|
||||
|
||||
// Reveal your hand: If you have seven or more land cards in your hand, flip Sasaya, Orochi Ascendant.
|
||||
// Sasaya's Essence: Legendary Enchantment
|
||||
// Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana of any type that land produced.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Sasaya, Orochi Ascendant", 1);
|
||||
// Mana pools don't empty as steps and phases end.
|
||||
addCard(Zone.HAND, playerA, "Upwelling", 1); // Enchantment {3}{G}
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reveal your hand: If you have seven or more land cards in your hand, flip");
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}");
|
||||
activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}");
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Upwelling");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Sasaya's Essence", 1);
|
||||
assertPermanentCount(playerA, "Upwelling", 1);
|
||||
|
||||
assertManaPool(playerA, ManaType.GREEN, 2);
|
||||
|
||||
assertLife(playerA, 20);
|
||||
|
||||
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
|
||||
assertDuplicatedManaOptions(manaOptions);
|
||||
|
||||
Assert.assertEquals("mana variations don't fit", 4, manaOptions.size());
|
||||
assertManaOptions("{R}{R}{R}{R}{G}{G}{G}", manaOptions);
|
||||
assertManaOptions("{R}{R}{R}{G}{G}{G}{G}", manaOptions);
|
||||
assertManaOptions("{R}{R}{G}{G}{G}{G}{G}", manaOptions);
|
||||
assertManaOptions("{R}{G}{G}{G}{G}{G}{G}", manaOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,7 +53,6 @@ public class ConditionalManaTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testWorkingWithReflectingPool() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Cavern of Souls", 1); // can give {C] or {any} mana ({any} with restrictions)
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); // must give {C} or {any} mana from the Cavern, but without restrictions
|
||||
|
@ -321,7 +320,6 @@ public class ConditionalManaTest extends CardTestPlayerBase {
|
|||
// and process all available net mana by special call like TriggeredManaAbility->getNetManaForEvent(ManaEvent xxx)
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void TriggeredManaAbilityMustGivesExtraManaOptions() {
|
||||
// TriggeredManaAbility must give extra mana options (2 red instead 1)
|
||||
// Whenever you tap a land for mana, add one mana of any type that land produced.
|
||||
|
@ -340,7 +338,6 @@ public class ConditionalManaTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void DictateOfKarametra_AutoPay() {
|
||||
// Whenever you tap a land for mana, add one mana of any type that land produced.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Dictate of Karametra");
|
||||
|
|
|
@ -5,7 +5,6 @@ import mage.constants.ManaType;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
@ -133,7 +132,6 @@ public class ReflectingPoolTest extends CardTestPlayerBase {
|
|||
* producing mana
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testWithDifferentLands() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
|
||||
|
@ -215,7 +213,6 @@ public class ReflectingPoolTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testReflectingPoolAnyManaNeedWithoutCondition() {
|
||||
// any mana source without conditions (use any mana at any time)
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
|
@ -233,7 +230,6 @@ public class ReflectingPoolTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testReflectingPoolAnyManaNeedWithCondition() {
|
||||
// any mana source have condition to use (Reflecting Pool must ignore that condition)
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Cavern of Souls", 1); // {C} or {any}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package org.mage.test.cards.mana;
|
||||
|
||||
import mage.abilities.mana.ManaOptions;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
import static org.mage.test.utils.ManaOptionsTestUtils.assertManaOptions;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class TappedForManaRelatedTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* This is a new rule that slightly changes how we resolve abilities that
|
||||
* trigger whenever a permanent is tapped for mana or for mana of a
|
||||
* specified type. Now, you look at what was actually produced after the
|
||||
* activated mana ability resolves. So, tapping a Gaea's Cradle while you no
|
||||
* control no creatures won't cause a Wild Growth attached to it to trigger.
|
||||
*/
|
||||
@Test
|
||||
public void TestCradleWithWildGrowthNoCreatures() {
|
||||
// {T}: Add {G} for each creature you control.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Gaea's Cradle", 1);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
// Enchant land
|
||||
// Whenever enchanted land is tapped for mana, its controller adds {G}.
|
||||
addCard(Zone.HAND, playerA, "Wild Growth", 1); // Enchantment {G}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wild Growth", "Gaea's Cradle");
|
||||
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Wild Growth", 1);
|
||||
|
||||
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
|
||||
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
|
||||
assertManaOptions("{G}{G}", manaOptions);
|
||||
|
||||
}
|
||||
|
||||
// Mana producedc by triggered mana abilities is not calculated in manaOptions calculations yet.
|
||||
@Test
|
||||
// @Ignore
|
||||
public void TestCradleWithWildGrowthTwoCreatures() {
|
||||
// {T}: Add {G} for each creature you control.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Gaea's Cradle", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
// Enchant land
|
||||
// Whenever enchanted land is tapped for mana, its controller adds {G}.
|
||||
addCard(Zone.HAND, playerA, "Wild Growth", 1); // Enchantment {G}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wild Growth", "Gaea's Cradle");
|
||||
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Wild Growth", 1);
|
||||
|
||||
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
|
||||
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
|
||||
assertManaOptions("{G}{G}{G}{G}", manaOptions);
|
||||
|
||||
}
|
||||
|
||||
// Mana producedc by triggered mana abilities is not calculated in manaOptions calculations yet.
|
||||
@Test
|
||||
// @Ignore
|
||||
public void TestWildGrowth() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
// Enchant land
|
||||
// Whenever enchanted land is tapped for mana, its controller adds {G}.
|
||||
addCard(Zone.HAND, playerA, "Wild Growth", 1); // Enchantment {G}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wild Growth", "Forest");
|
||||
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Wild Growth", 1);
|
||||
|
||||
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
|
||||
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
|
||||
assertManaOptions("{G}{G}", manaOptions);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -63,6 +63,7 @@ import java.util.*;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.Mana;
|
||||
|
||||
import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*;
|
||||
|
||||
|
@ -3274,7 +3275,17 @@ public class TestPlayer implements Player {
|
|||
public ManaOptions getManaAvailable(Game game) {
|
||||
return computerPlayer.getManaAvailable(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAvailableTriggeredMana(List<Mana> availableTriggeredMana) {
|
||||
computerPlayer.addAvailableTriggeredMana(availableTriggeredMana);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<List<Mana>> getAvailableTriggeredMana() {
|
||||
return computerPlayer.getAvailableTriggeredMana();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
|
||||
return computerPlayer.getPlayable(game, hidden);
|
||||
|
|
|
@ -41,6 +41,7 @@ import mage.target.common.TargetCardInLibrary;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import mage.Mana;
|
||||
|
||||
/**
|
||||
* @author Quercitron
|
||||
|
@ -1037,6 +1038,21 @@ public class PlayerStub implements Player {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAvailableTriggeredMana(List<Mana> availableTriggeredMan) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<List<Mana>> getAvailableTriggeredMana() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
|
||||
return null;
|
||||
|
|
|
@ -209,11 +209,32 @@ public class ManaOptionsTest extends CardTestPlayerBase {
|
|||
assertManaOptions("{C}{G}{Any}", manaOptions);
|
||||
}
|
||||
|
||||
// Nykthos, Shrine to Nyx
|
||||
@Test
|
||||
public void testNykthos4a() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Sedge Scorpion", 4);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||
// {T}: Add {C}.
|
||||
// {2}, {T}: Choose a color. Add an amount of mana of that color equal to your devotion to that color. (Your devotion to a color is the number of mana symbols of that color in the mana costs of permanents you control.)
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Nykthos, Shrine to Nyx", 1);
|
||||
|
||||
setStopAt(1, PhaseStep.UPKEEP);
|
||||
execute();
|
||||
|
||||
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
|
||||
assertDuplicatedManaOptions(manaOptions);
|
||||
|
||||
Assert.assertEquals("mana variations don't fit", 2, manaOptions.size());
|
||||
assertManaOptions("{C}{G}{G}{G}", manaOptions);
|
||||
assertManaOptions("{G}{G}{G}{G}{G}", manaOptions);
|
||||
|
||||
}
|
||||
|
||||
// Nykthos, Shrine to Nyx
|
||||
// {T}: Add {C}.
|
||||
// {2}, {T}: Choose a color. Add an amount of mana of that color equal to your devotion to that color. (Your devotion to a color is the number of mana symbols of that color in the mana costs of permanents you control.)
|
||||
@Test
|
||||
public void testNykthos4() {
|
||||
public void testNykthos4b() {
|
||||
// If a land is tapped for two or more mana, it produces {C} instead of any other type and amount.
|
||||
// Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Damping Sphere", 1);
|
||||
|
@ -399,7 +420,6 @@ public class ManaOptionsTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore // TriggeredManaAbilities not supported yet for getAvailableMana
|
||||
public void testCryptGhast() {
|
||||
//Extort (Whenever you cast a spell, you may pay {WB}. If you do, each opponent loses 1 life and you gain that much life.)
|
||||
// Whenever you tap a Swamp for mana, add {B} (in addition to the mana the land produces).
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.util.ArrayList;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.effects.common.ManaEffect;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
@ -171,6 +172,9 @@ public abstract class AbilityImpl implements Ability {
|
|||
private boolean resolveMode(Game game) {
|
||||
boolean result = true;
|
||||
for (Effect effect : getEffects()) {
|
||||
if (game.inCheckPlayableState() && !(effect instanceof ManaEffect)) {
|
||||
continue; // Ignored non mana effects - see GameEvent.TAPPED_FOR_MANA
|
||||
}
|
||||
if (effect instanceof OneShotEffect) {
|
||||
boolean effectResult = effect.apply(game, this);
|
||||
result &= effectResult;
|
||||
|
|
|
@ -41,6 +41,9 @@ public class TapForManaAllTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) {
|
||||
ManaEvent mEvent = (ManaEvent) event;
|
||||
|
|
|
@ -34,6 +34,9 @@ public class TapLandForManaAllTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.inCheckPlayableState()) { // Ignored - see GameEvent.TAPPED_FOR_MANA
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
if (permanent != null && permanent.isLand()) {
|
||||
if (setTargetPointer) {
|
||||
|
|
|
@ -26,7 +26,7 @@ public class ReturnToHandChosenControlledPermanentCost extends CostImpl {
|
|||
target.setNotTarget(true);
|
||||
this.addTarget(target);
|
||||
if (target.getMaxNumberOfTargets() > 1 && target.getMaxNumberOfTargets() == target.getNumberOfTargets()) {
|
||||
this.text = "return " + CardUtil.numberToText(target.getMaxNumberOfTargets()) + ' '
|
||||
this.text = "Return " + CardUtil.numberToText(target.getMaxNumberOfTargets()) + ' '
|
||||
+ target.getTargetName()
|
||||
+ (target.getTargetName().endsWith(" you control") ? "" : " you control")
|
||||
+ " to their owner's hand";
|
||||
|
|
|
@ -14,6 +14,7 @@ import mage.players.Player;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
@ -38,6 +39,15 @@ public abstract class ManaEffect extends OneShotEffect {
|
|||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
if (game.inCheckPlayableState()) {
|
||||
// During calculation of the available mana for a player the "TappedForMana" event is fired to simulate triggered mana production.
|
||||
// By checking the inCheckPlayableState these events are handled to give back only the available mana of instead really producing mana
|
||||
// So it's important if ManaEffects overwrite the apply method to take care for this.
|
||||
if (source instanceof TriggeredAbility) {
|
||||
player.addAvailableTriggeredMana(getNetMana(game, source));
|
||||
}
|
||||
return true; // No need to add mana to pool during checkPlayable
|
||||
}
|
||||
Mana manaToAdd = produceMana(game, source);
|
||||
if (manaToAdd != null && manaToAdd.count() > 0) {
|
||||
checkToFirePossibleEvents(manaToAdd, game, source);
|
||||
|
@ -72,11 +82,13 @@ public abstract class ManaEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
/**
|
||||
* Produced the mana the effect can produce (DO NOT add it to mana pool -- return all added as mana object to process by replace events)
|
||||
* Produced the mana the effect can produce (DO NOT add it to mana pool --
|
||||
* return all added as mana object to process by replace events)
|
||||
* <p>
|
||||
* WARNING, produceMana can be called multiple times for mana and spell available calculations
|
||||
* if you don't want it then overide getNetMana to return max possible mana values
|
||||
* (if you have choose dialogs or extra effects like new counters in produceMana)
|
||||
* WARNING, produceMana can be called multiple times for mana and spell
|
||||
* available calculations if you don't want it then overide getNetMana to
|
||||
* return max possible mana values (if you have choose dialogs or extra
|
||||
* effects like new counters in produceMana)
|
||||
*
|
||||
* @param game warning, can be NULL for AI score calcs (game == null)
|
||||
* @param source
|
||||
|
|
|
@ -38,9 +38,26 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect {
|
|||
@Override
|
||||
public List<Mana> getNetMana(Game game, Ability source) {
|
||||
List<Mana> netMana = new ArrayList<>();
|
||||
Mana types = (Mana) this.getValue("mana"); // TODO: will not work until TriggeredManaAbility fix (see TriggeredManaAbilityMustGivesExtraManaOptions test)
|
||||
Mana types = (Mana) this.getValue("mana");
|
||||
if (types != null) {
|
||||
netMana.add(types.copy());
|
||||
if (types.getBlack() > 0) {
|
||||
netMana.add(Mana.BlackMana(1));
|
||||
}
|
||||
if (types.getRed() > 0) {
|
||||
netMana.add(Mana.RedMana(1));
|
||||
}
|
||||
if (types.getBlue() > 0) {
|
||||
netMana.add(Mana.BlueMana(1));
|
||||
}
|
||||
if (types.getGreen() > 0) {
|
||||
netMana.add(Mana.GreenMana(1));
|
||||
}
|
||||
if (types.getWhite() > 0) {
|
||||
netMana.add(Mana.WhiteMana(1));
|
||||
}
|
||||
if (types.getColorless() > 0) {
|
||||
netMana.add(Mana.ColorlessMana(1));
|
||||
}
|
||||
}
|
||||
return netMana;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import mage.Mana;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.common.ManaEffect;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
public class BasicManaEffect extends ManaEffect {
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import mage.abilities.costs.common.TapSourceCost;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ManaEvent;
|
||||
import mage.players.Player;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
|
@ -44,24 +45,11 @@ public class ManaOptions extends ArrayList<Mana> {
|
|||
//if there is only one mana option available add it to all the existing options
|
||||
List<Mana> netManas = abilities.get(0).getNetMana(game);
|
||||
if (netManas.size() == 1) {
|
||||
if (!hasTapCost(abilities.get(0)) || checkTappedForManaReplacement(abilities.get(0), game, netManas.get(0))) {
|
||||
addMana(netManas.get(0));
|
||||
}
|
||||
checkTappedForManaReplacement(abilities.get(0), game, netManas.get(0));
|
||||
addMana(netManas.get(0));
|
||||
addTriggeredMana(game, abilities.get(0));
|
||||
} else if (netManas.size() > 1) {
|
||||
List<Mana> copy = copy();
|
||||
this.clear();
|
||||
// boolean hasTapCost = hasTapCost(abilities.get(0)); // needed if checkTappedForManaReplacement is reactivated
|
||||
for (Mana netMana : netManas) {
|
||||
for (Mana mana : copy) {
|
||||
// checkTappedForManaReplacement seems in some situations to produce endless iterations so deactivated for now: https://github.com/magefree/mage/issues/5023
|
||||
if (true/* !hasTapCost || checkTappedForManaReplacement(abilities.get(0), game, netMana) */) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(mana);
|
||||
newMana.add(netMana);
|
||||
this.add(newMana);
|
||||
}
|
||||
}
|
||||
}
|
||||
addManaVariation(netManas, abilities.get(0), game);
|
||||
}
|
||||
|
||||
} else { // mana source has more than 1 ability
|
||||
|
@ -69,14 +57,14 @@ public class ManaOptions extends ArrayList<Mana> {
|
|||
List<Mana> copy = copy();
|
||||
this.clear();
|
||||
for (ActivatedManaAbilityImpl ability : abilities) {
|
||||
boolean hasTapCost = hasTapCost(ability);
|
||||
for (Mana netMana : ability.getNetMana(game)) {
|
||||
if (!hasTapCost || checkTappedForManaReplacement(ability, game, netMana)) {
|
||||
checkTappedForManaReplacement(ability, game, netMana);
|
||||
for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) {
|
||||
SkipAddMana:
|
||||
for (Mana mana : copy) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(mana);
|
||||
newMana.add(netMana);
|
||||
newMana.add(triggeredManaVariation);
|
||||
for (Mana existingMana : this) {
|
||||
if (existingMana.equalManaValue(newMana)) {
|
||||
continue SkipAddMana;
|
||||
|
@ -91,18 +79,48 @@ public class ManaOptions extends ArrayList<Mana> {
|
|||
this.add(newMana);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkTappedForManaReplacement(Ability ability, Game game, Mana mana) {
|
||||
ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, ability.getSourceId(), ability.getSourceId(), ability.getControllerId(), mana);
|
||||
if (!game.replaceEvent(event)) {
|
||||
return true;
|
||||
private void addManaVariation(List<Mana> netManas, ActivatedManaAbilityImpl ability, Game game) {
|
||||
List<Mana> copy = copy();
|
||||
this.clear();
|
||||
for (Mana netMana : netManas) {
|
||||
for (Mana mana : copy) {
|
||||
if (!hasTapCost(ability) || checkTappedForManaReplacement(ability, game, netMana)) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(mana);
|
||||
newMana.add(netMana);
|
||||
this.add(newMana);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<List<Mana>> getSimulatedTriggeredManaFromPlayer(Game game, Ability ability) {
|
||||
Player player = game.getPlayer(ability.getControllerId());
|
||||
List<List<Mana>> newList = new ArrayList<>();
|
||||
if (player != null) {
|
||||
newList.addAll(player.getAvailableTriggeredMana());
|
||||
player.getAvailableTriggeredMana().clear();
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
|
||||
private boolean checkTappedForManaReplacement(Ability ability, Game game, Mana mana) {
|
||||
if (hasTapCost(ability)) {
|
||||
ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, ability.getSourceId(), ability.getSourceId(), ability.getControllerId(), mana);
|
||||
if (!game.replaceEvent(event)) {
|
||||
game.fireEvent(event);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean hasTapCost(Ability ability) {
|
||||
|
@ -127,31 +145,41 @@ public class ManaOptions extends ArrayList<Mana> {
|
|||
// no mana costs
|
||||
if (ability.getManaCosts().isEmpty()) {
|
||||
if (netManas.size() == 1) {
|
||||
checkTappedForManaReplacement(ability, game, netManas.get(0));
|
||||
addMana(netManas.get(0));
|
||||
addTriggeredMana(game, ability);
|
||||
} else {
|
||||
List<Mana> copy = copy();
|
||||
this.clear();
|
||||
for (Mana netMana : netManas) {
|
||||
for (Mana mana : copy) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(mana);
|
||||
newMana.add(netMana);
|
||||
this.add(newMana);
|
||||
checkTappedForManaReplacement(ability, game, netMana);
|
||||
for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) {
|
||||
for (Mana mana : copy) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(mana);
|
||||
newMana.add(triggeredManaVariation);
|
||||
this.add(newMana);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else // the ability has mana costs
|
||||
if (netManas.size() == 1) {
|
||||
checkTappedForManaReplacement(ability, game, netManas.get(0));
|
||||
subtractCostAddMana(ability.getManaCosts().getMana(), netManas.get(0), ability.getCosts().isEmpty());
|
||||
addTriggeredMana(game, ability);
|
||||
} else {
|
||||
List<Mana> copy = copy();
|
||||
this.clear();
|
||||
for (Mana netMana : netManas) {
|
||||
for (Mana mana : copy) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(mana);
|
||||
newMana.add(netMana);
|
||||
subtractCostAddMana(ability.getManaCosts().getMana(), netMana, ability.getCosts().isEmpty());
|
||||
checkTappedForManaReplacement(ability, game, netMana);
|
||||
for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) {
|
||||
for (Mana mana : copy) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(mana);
|
||||
newMana.add(triggeredManaVariation);
|
||||
subtractCostAddMana(ability.getManaCosts().getMana(), netMana, ability.getCosts().isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,30 +188,30 @@ public class ManaOptions extends ArrayList<Mana> {
|
|||
List<Mana> copy = copy();
|
||||
this.clear();
|
||||
for (ActivatedManaAbilityImpl ability : abilities) {
|
||||
boolean hasTapCost = hasTapCost(ability);
|
||||
List<Mana> netManas = ability.getNetMana(game);
|
||||
|
||||
if (ability.getManaCosts().isEmpty()) {
|
||||
for (Mana netMana : netManas) {
|
||||
if (!hasTapCost || checkTappedForManaReplacement(ability, game, netMana)) {
|
||||
checkTappedForManaReplacement(ability, game, netMana);
|
||||
for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) {
|
||||
for (Mana mana : copy) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(mana);
|
||||
newMana.add(netMana);
|
||||
newMana.add(triggeredManaVariation);
|
||||
this.add(newMana);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Mana netMana : netManas) {
|
||||
if (!hasTapCost || checkTappedForManaReplacement(ability, game, netMana)) {
|
||||
checkTappedForManaReplacement(ability, game, netMana);
|
||||
for (Mana triggeredManaVariation : getTriggeredManaVariations(game, ability, netMana)) {
|
||||
for (Mana previousMana : copy) {
|
||||
CombineWithExisting:
|
||||
for (Mana manaOption : ability.getManaCosts().getManaOptions()) {
|
||||
Mana newMana = new Mana(previousMana);
|
||||
if (previousMana.includesMana(manaOption)) { // costs can be paid
|
||||
newMana.subtractCost(manaOption);
|
||||
newMana.add(netMana);
|
||||
newMana.add(triggeredManaVariation);
|
||||
// if the new mana is in all colors more than another already existing than replace
|
||||
for (Mana existingMana : this) {
|
||||
Mana moreValuable = Mana.getMoreValuableMana(newMana, existingMana);
|
||||
|
@ -211,6 +239,52 @@ public class ManaOptions extends ArrayList<Mana> {
|
|||
}
|
||||
}
|
||||
|
||||
private List<Mana> getTriggeredManaVariations(Game game, Ability ability, Mana baseMana) {
|
||||
List<Mana> baseManaPlusTriggeredMana = new ArrayList<>();
|
||||
baseManaPlusTriggeredMana.add(baseMana);
|
||||
List<List<Mana>> availableTriggeredManaList = getSimulatedTriggeredManaFromPlayer(game, ability);
|
||||
for (List<Mana> availableTriggeredMana : availableTriggeredManaList) {
|
||||
if (availableTriggeredMana.size() == 1) {
|
||||
for (Mana prevMana : baseManaPlusTriggeredMana) {
|
||||
prevMana.add(availableTriggeredMana.get(0));
|
||||
}
|
||||
} else if (availableTriggeredMana.size() > 1) {
|
||||
List<Mana> copy = new ArrayList<>(baseManaPlusTriggeredMana);
|
||||
baseManaPlusTriggeredMana.clear();
|
||||
for (Mana triggeredMana : availableTriggeredMana) {
|
||||
for (Mana prevMana : copy) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(prevMana);
|
||||
newMana.add(triggeredMana);
|
||||
baseManaPlusTriggeredMana.add(newMana);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return baseManaPlusTriggeredMana;
|
||||
}
|
||||
|
||||
private void addTriggeredMana(Game game, Ability ability) {
|
||||
List<List<Mana>> netManaList = getSimulatedTriggeredManaFromPlayer(game, ability);
|
||||
for (List<Mana> triggeredNetMana : netManaList) {
|
||||
if (triggeredNetMana.size() == 1) {
|
||||
addMana(triggeredNetMana.get(0));
|
||||
} else if (triggeredNetMana.size() > 1) {
|
||||
// Add variations
|
||||
List<Mana> copy = copy();
|
||||
this.clear();
|
||||
for (Mana triggeredMana : triggeredNetMana) {
|
||||
for (Mana mana : copy) {
|
||||
Mana newMana = new Mana();
|
||||
newMana.add(mana);
|
||||
newMana.add(triggeredMana);
|
||||
this.add(newMana);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addMana(Mana addMana) {
|
||||
if (isEmpty()) {
|
||||
this.add(new Mana());
|
||||
|
|
|
@ -133,7 +133,7 @@ public class GameEvent implements Serializable {
|
|||
targetId id of the spell that's cast
|
||||
playerId player that casts the spell or ability
|
||||
amount X multiplier to change X value, default 1
|
||||
*/
|
||||
*/
|
||||
CAST_SPELL,
|
||||
/* SPELL_CAST
|
||||
x-Costs are already defined
|
||||
|
@ -153,13 +153,13 @@ public class GameEvent implements Serializable {
|
|||
targetId id of the ability to activate / use
|
||||
sourceId sourceId of the object with that ability
|
||||
playerId player that tries to use this ability
|
||||
*/
|
||||
*/
|
||||
TAKE_SPECIAL_ACTION, TAKEN_SPECIAL_ACTION, // not used in implementation yet
|
||||
/* TAKE_SPECIAL_ACTION, TAKEN_SPECIAL_ACTION,
|
||||
targetId id of the ability to activate / use
|
||||
sourceId sourceId of the object with that ability
|
||||
playerId player that tries to use this ability
|
||||
*/
|
||||
*/
|
||||
TRIGGERED_ABILITY,
|
||||
RESOLVING_ABILITY,
|
||||
COPY_STACKOBJECT, COPIED_STACKOBJECT,
|
||||
|
@ -254,7 +254,13 @@ public class GameEvent implements Serializable {
|
|||
ENTERS_THE_BATTLEFIELD_CONTROL, // 616.1b
|
||||
ENTERS_THE_BATTLEFIELD_COPY, // 616.1c
|
||||
ENTERS_THE_BATTLEFIELD, // 616.1d
|
||||
TAP, TAPPED, TAPPED_FOR_MANA,
|
||||
TAP, TAPPED,
|
||||
TAPPED_FOR_MANA,
|
||||
/* TAPPED_FOR_MANA
|
||||
During calculation of the available mana for a player the "TappedForMana" event is fired to simulate triggered mana production.
|
||||
By checking the inCheckPlayableState these events are handled to give back only the available mana of instead really producing mana.
|
||||
IMPORTANT: Triggered non mana abilities have to ignore the event if game.inCheckPlayableState is true.
|
||||
*/
|
||||
UNTAP, UNTAPPED,
|
||||
FLIP, FLIPPED,
|
||||
UNFLIP, UNFLIPPED,
|
||||
|
@ -412,12 +418,12 @@ public class GameEvent implements Serializable {
|
|||
}
|
||||
|
||||
private GameEvent(EventType type, UUID customEventType,
|
||||
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
|
||||
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
|
||||
this(type, customEventType, targetId, sourceId, playerId, amount, flag, null);
|
||||
}
|
||||
|
||||
private GameEvent(EventType type, UUID customEventType,
|
||||
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag, MageObjectReference reference) {
|
||||
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag, MageObjectReference reference) {
|
||||
this.type = type;
|
||||
this.customEventType = customEventType;
|
||||
this.targetId = targetId;
|
||||
|
|
|
@ -40,6 +40,7 @@ import mage.util.Copyable;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import mage.Mana;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
@ -635,6 +636,10 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
void untap(Game game);
|
||||
|
||||
ManaOptions getManaAvailable(Game game);
|
||||
|
||||
void addAvailableTriggeredMana(List<Mana> netManaAvailable);
|
||||
|
||||
List<List<Mana>> getAvailableTriggeredMana();
|
||||
|
||||
List<ActivatedAbility> getPlayable(Game game, boolean hidden);
|
||||
|
||||
|
|
|
@ -177,6 +177,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
protected FilterMana phyrexianColors;
|
||||
|
||||
// Used during available mana calculation to give back possible available net mana from triggered mana abilities (No need to copy)
|
||||
protected final List<List<Mana>> availableTriggeredManaList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* During some steps we can't play anything
|
||||
*/
|
||||
|
@ -2848,8 +2851,18 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return game.getBattlefield().getAllActivePermanents(blockFilter, playerId, game);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mana options the player currently has. That means which combinations of
|
||||
* mana are available to cast spells or activate abilities etc.
|
||||
*
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ManaOptions getManaAvailable(Game game) {
|
||||
boolean oldState = game.inCheckPlayableState();
|
||||
game.setCheckPlayableState(true);
|
||||
|
||||
ManaOptions availableMana = new ManaOptions();
|
||||
|
||||
List<Abilities<ActivatedManaAbilityImpl>> sourceWithoutManaCosts = new ArrayList<>();
|
||||
|
@ -2891,10 +2904,34 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
// remove duplicated variants (see ManaOptionsTest for info - when that rises)
|
||||
availableMana.removeDuplicated();
|
||||
|
||||
|
||||
game.setCheckPlayableState(oldState);
|
||||
return availableMana;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used during calculation of available mana to gather the amount of producable triggered mana caused by using mana sources.
|
||||
* So the set value is only used during the calculation of the mana produced by one source and cleared thereafter
|
||||
*
|
||||
* @param netManaAvailable the net mana produced by the triggered mana abaility
|
||||
*/
|
||||
@Override
|
||||
public void addAvailableTriggeredMana(List<Mana> netManaAvailable) {
|
||||
this.availableTriggeredManaList.add(netManaAvailable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used during calculation of available mana to get the amount of producable triggered mana caused by using mana sources.
|
||||
* The list is cleared as soon the value is retrieved during available mana calculation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<List<Mana>> getAvailableTriggeredMana() {
|
||||
return availableTriggeredManaList;
|
||||
}
|
||||
|
||||
|
||||
// returns only mana producers that don't require mana payment
|
||||
protected List<MageObject> getAvailableManaProducers(Game game) {
|
||||
List<MageObject> result = new ArrayList<>();
|
||||
|
|
Loading…
Reference in a new issue