mirror of
https://github.com/correl/mage.git
synced 2024-12-24 11:50:45 +00:00
* AI: improved support of "as though mana" abilities (now computer can choose correct mana ability to pay, example: Draugr Necromancer);
* Dev: added card's LKI support of multi part cards (mdf/split/adventure); * Dev: improved support of adding/removing counters from mdf cards; * Draugr Necromancer - fixed support of mdf/split/adventure cards (#7620);
This commit is contained in:
parent
adc945748b
commit
dda69cd009
12 changed files with 227 additions and 65 deletions
|
@ -44,6 +44,7 @@ import mage.game.tournament.Tournament;
|
||||||
import mage.player.ai.simulators.CombatGroupSimulator;
|
import mage.player.ai.simulators.CombatGroupSimulator;
|
||||||
import mage.player.ai.simulators.CombatSimulator;
|
import mage.player.ai.simulators.CombatSimulator;
|
||||||
import mage.player.ai.simulators.CreatureSimulator;
|
import mage.player.ai.simulators.CreatureSimulator;
|
||||||
|
import mage.players.ManaPoolItem;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.players.PlayerImpl;
|
import mage.players.PlayerImpl;
|
||||||
import mage.players.net.UserData;
|
import mage.players.net.UserData;
|
||||||
|
@ -1423,6 +1424,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (activateAbility(manaAbility, game)) {
|
if (activateAbility(manaAbility, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1441,6 +1445,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (activateAbility(manaAbility, game)) {
|
if (activateAbility(manaAbility, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1456,6 +1463,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (activateAbility(manaAbility, game)) {
|
if (activateAbility(manaAbility, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1471,6 +1481,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (activateAbility(manaAbility, game)) {
|
if (activateAbility(manaAbility, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1486,6 +1499,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (activateAbility(manaAbility, game)) {
|
if (activateAbility(manaAbility, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1501,6 +1517,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (activateAbility(manaAbility, game)) {
|
if (activateAbility(manaAbility, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1516,6 +1535,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (approvingObject != null && !canUseAsThoughManaToPayManaCost(cost, ability, netMana, manaAbility, mageObject, game)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (activateAbility(manaAbility, game)) {
|
if (activateAbility(manaAbility, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1555,6 +1577,56 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean canUseAsThoughManaToPayManaCost(ManaCost checkCost, Ability abilityToPay, Mana manaOption, Ability manaAbility, MageObject manaProducer, Game game) {
|
||||||
|
// asThoughMana can change producing mana type, so you must check it here
|
||||||
|
// cause some effects adds additional checks in getAsThoughManaType (example: Draugr Necromancer with snow mana sources)
|
||||||
|
|
||||||
|
// simulate real asThoughMana usage
|
||||||
|
ManaPoolItem possiblePoolItem;
|
||||||
|
if (manaOption instanceof ConditionalMana) {
|
||||||
|
ConditionalMana conditionalNetMana = (ConditionalMana) manaOption;
|
||||||
|
possiblePoolItem = new ManaPoolItem(
|
||||||
|
conditionalNetMana,
|
||||||
|
manaAbility.getSourceObject(game),
|
||||||
|
conditionalNetMana.getManaProducerOriginalId() != null ? conditionalNetMana.getManaProducerOriginalId() : manaAbility.getOriginalId()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
possiblePoolItem = new ManaPoolItem(
|
||||||
|
manaOption.getRed(),
|
||||||
|
manaOption.getGreen(),
|
||||||
|
manaOption.getBlue(),
|
||||||
|
manaOption.getWhite(),
|
||||||
|
manaOption.getBlack(),
|
||||||
|
manaOption.getGeneric() + manaOption.getColorless(),
|
||||||
|
manaProducer,
|
||||||
|
manaAbility.getOriginalId(),
|
||||||
|
manaOption.getFlag()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cost can contains multiple mana types, must check each type (is it possible to pay a cost)
|
||||||
|
for (ManaType checkType : ManaUtil.getManaTypesInCost(checkCost)) {
|
||||||
|
// affected asThoughMana effect must fit a checkType with pool mana
|
||||||
|
ManaType possibleAsThoughPoolManaType = game.getContinuousEffects().asThoughMana(checkType, possiblePoolItem, abilityToPay.getSourceId(), abilityToPay, abilityToPay.getControllerId(), game);
|
||||||
|
if (possibleAsThoughPoolManaType == null) {
|
||||||
|
continue; // no affected asThough effects
|
||||||
|
}
|
||||||
|
boolean canPay;
|
||||||
|
if (possibleAsThoughPoolManaType == ManaType.COLORLESS) {
|
||||||
|
// colorless can be payed by any color from the pool
|
||||||
|
canPay = possiblePoolItem.count() > 0;
|
||||||
|
} else {
|
||||||
|
// colored must be payed by specific color from the pool (AsThough already changed it to fit with mana pool)
|
||||||
|
canPay = possiblePoolItem.get(possibleAsThoughPoolManaType) > 0;
|
||||||
|
}
|
||||||
|
if (canPay) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private Abilities<ActivatedManaAbilityImpl> getManaAbilitiesSortedByManaCount(MageObject mageObject, final Game game) {
|
private Abilities<ActivatedManaAbilityImpl> getManaAbilitiesSortedByManaCount(MageObject mageObject, final Game game) {
|
||||||
Abilities<ActivatedManaAbilityImpl> manaAbilities = mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, playerId, game);
|
Abilities<ActivatedManaAbilityImpl> manaAbilities = mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, playerId, game);
|
||||||
if (manaAbilities.size() > 1) {
|
if (manaAbilities.size() > 1) {
|
||||||
|
|
|
@ -88,7 +88,7 @@ class CelestialDawnToPlainsEffect extends ContinuousEffectImpl {
|
||||||
land.addAbility(new WhiteManaAbility(), source.getSourceId(), game);
|
land.addAbility(new WhiteManaAbility(), source.getSourceId(), game);
|
||||||
break;
|
break;
|
||||||
case TypeChangingEffects_4:
|
case TypeChangingEffects_4:
|
||||||
land.removeAllSubTypes(game,SubTypeSet.NonBasicLandType);
|
land.removeAllSubTypes(game, SubTypeSet.NonBasicLandType);
|
||||||
land.addSubType(game, SubType.PLAINS);
|
land.addSubType(game, SubType.PLAINS);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -241,7 +241,9 @@ class CelestialDawnSpendColorlessManaEffect extends AsThoughEffectImpl implement
|
||||||
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
|
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
|
||||||
if (mana.getWhite() == 0) {
|
if (mana.getWhite() == 0) {
|
||||||
return ManaType.COLORLESS;
|
return ManaType.COLORLESS;
|
||||||
|
} else {
|
||||||
|
// must return manaType cause applied all the time
|
||||||
|
return manaType;
|
||||||
}
|
}
|
||||||
return manaType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,7 @@ import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.AsThoughEffectImpl;
|
import mage.abilities.effects.AsThoughEffectImpl;
|
||||||
import mage.abilities.effects.AsThoughManaEffect;
|
import mage.abilities.effects.AsThoughManaEffect;
|
||||||
import mage.abilities.effects.ReplacementEffectImpl;
|
import mage.abilities.effects.ReplacementEffectImpl;
|
||||||
import mage.cards.Card;
|
import mage.cards.*;
|
||||||
import mage.cards.CardImpl;
|
|
||||||
import mage.cards.CardSetInfo;
|
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import mage.game.CardState;
|
import mage.game.CardState;
|
||||||
|
@ -19,7 +17,6 @@ import mage.game.permanent.Permanent;
|
||||||
import mage.game.permanent.PermanentToken;
|
import mage.game.permanent.PermanentToken;
|
||||||
import mage.players.ManaPoolItem;
|
import mage.players.ManaPoolItem;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.util.CardUtil;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -167,14 +164,32 @@ class DraugrNecromancerSpendAnyManaEffect extends AsThoughEffectImpl implements
|
||||||
|| !game.getOpponents(game.getOwnerId(sourceId)).contains(source.getControllerId())) {
|
|| !game.getOpponents(game.getOwnerId(sourceId)).contains(source.getControllerId())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card card = game.getCard(sourceId);
|
Card card = game.getCard(sourceId);
|
||||||
if (card != null
|
if (card == null) {
|
||||||
&& game.getState().getZone(card.getId()) == Zone.EXILED
|
return false;
|
||||||
&& card.getMainCard().getCounters(game).getCount(CounterType.ICE) > 0) {
|
}
|
||||||
return true;
|
card = card.getMainCard();
|
||||||
|
|
||||||
|
// card can be in exile or stack zones
|
||||||
|
if (game.getState().getZone(card.getId()) == Zone.EXILED) {
|
||||||
|
// exile zone
|
||||||
|
return card.getCounters(game).getCount(CounterType.ICE) > 0;
|
||||||
|
} else {
|
||||||
|
// stack zone
|
||||||
|
// you must look at exile zone (use LKI to see ice counters from the past)
|
||||||
|
CardState cardState;
|
||||||
|
if (card instanceof SplitCard) {
|
||||||
|
cardState = game.getLastKnownInformationCard(card.getId(), Zone.EXILED);
|
||||||
|
} else if (card instanceof AdventureCard) {
|
||||||
|
cardState = game.getLastKnownInformationCard(card.getId(), Zone.EXILED);
|
||||||
|
} else if (card instanceof ModalDoubleFacesCard) {
|
||||||
|
cardState = game.getLastKnownInformationCard(((ModalDoubleFacesCard) card).getLeftHalfCard().getId(), Zone.EXILED);
|
||||||
|
} else {
|
||||||
|
cardState = game.getLastKnownInformationCard(card.getId(), Zone.EXILED);
|
||||||
|
}
|
||||||
|
return cardState != null && cardState.getCounters().getCount(CounterType.ICE) > 0;
|
||||||
}
|
}
|
||||||
CardState cardState = game.getLastKnownInformationCard(CardUtil.getMainCardId(game, sourceId), Zone.EXILED);
|
|
||||||
return cardState != null && cardState.getCounters().getCount(CounterType.ICE) > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -182,6 +197,6 @@ class DraugrNecromancerSpendAnyManaEffect extends AsThoughEffectImpl implements
|
||||||
if (mana.getSourceObject().isSnow()) {
|
if (mana.getSourceObject().isSnow()) {
|
||||||
return mana.getFirstAvailable();
|
return mana.getFirstAvailable();
|
||||||
}
|
}
|
||||||
return manaType;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,24 @@
|
||||||
|
|
||||||
package mage.cards.s;
|
package mage.cards.s;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.AsThoughEffectImpl;
|
import mage.abilities.effects.AsThoughEffectImpl;
|
||||||
import mage.abilities.effects.AsThoughManaEffect;
|
import mage.abilities.effects.AsThoughManaEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.AsThoughEffectType;
|
import mage.constants.*;
|
||||||
import mage.constants.CardType;
|
|
||||||
import mage.constants.Duration;
|
|
||||||
import mage.constants.ManaType;
|
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.ManaPoolItem;
|
import mage.players.ManaPoolItem;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public final class SunglassesOfUrza extends CardImpl {
|
public final class SunglassesOfUrza extends CardImpl {
|
||||||
|
|
||||||
public SunglassesOfUrza(UUID ownerId, CardSetInfo setInfo) {
|
public SunglassesOfUrza(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
|
||||||
|
|
||||||
// You may spend white mana as though it were red mana.
|
// You may spend white mana as though it were red mana.
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SunglassesOfUrzaManaAsThoughtEffect()));
|
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SunglassesOfUrzaManaAsThoughtEffect()));
|
||||||
|
@ -62,7 +56,7 @@ class SunglassesOfUrzaManaAsThoughtEffect extends AsThoughEffectImpl implements
|
||||||
if (mana.getWhite() > 0 && ManaType.RED == manaType) {
|
if (mana.getWhite() > 0 && ManaType.RED == manaType) {
|
||||||
return ManaType.WHITE;
|
return ManaType.WHITE;
|
||||||
}
|
}
|
||||||
return manaType;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package org.mage.test.cards.asthough;
|
||||||
|
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class AsThoughManaAndAITest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AutoPaymentMustUseAsThoughMana() {
|
||||||
|
// possible bug: AI auto-payment uses first mana ability from the mana source, so multi-colored lands can be broken
|
||||||
|
|
||||||
|
// You may spend white mana as though it were red mana.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Sunglasses of Urza", 1);
|
||||||
|
//
|
||||||
|
// {T}: Add {U} or {W}.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Sea of Clouds", 1);
|
||||||
|
addCard(Zone.HAND, playerA, "Lightning Bolt", 1); // {R}
|
||||||
|
|
||||||
|
checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Lightning Bolt", true);
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerB, 20 - 3);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ package org.mage.test.cards.single.khm;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
@ -186,16 +185,16 @@ public class DraugrNecromancerTest extends CardTestPlayerBase {
|
||||||
@Test
|
@Test
|
||||||
public void testCastMDFCFrontFromExile() {
|
public void testCastMDFCFrontFromExile() {
|
||||||
addCard(Zone.BATTLEFIELD, playerA, necromancer);
|
addCard(Zone.BATTLEFIELD, playerA, necromancer);
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Badlands", 6);
|
addCard(Zone.BATTLEFIELD, playerA, "Badlands", 6); // {B} or {R}
|
||||||
addCard(Zone.HAND, playerA, murder);
|
addCard(Zone.HAND, playerA, murder);
|
||||||
addCard(Zone.HAND, playerB, birgi);
|
addCard(Zone.HAND, playerB, birgi);
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3);
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3);
|
||||||
|
|
||||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, birgi);
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, birgi);
|
||||||
|
|
||||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, murder, birgi);
|
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, murder, birgi); // {1}{B}{B}
|
||||||
|
|
||||||
castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, birgi);
|
castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, birgi); // {2}{R}
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(3, PhaseStep.END_TURN);
|
setStopAt(3, PhaseStep.END_TURN);
|
||||||
|
@ -232,7 +231,6 @@ public class DraugrNecromancerTest extends CardTestPlayerBase {
|
||||||
assertExileCount(playerB, birgi, 0);
|
assertExileCount(playerB, birgi, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testCastMDFCFrontFromExileWithSnow() {
|
public void testCastMDFCFrontFromExileWithSnow() {
|
||||||
addCard(Zone.BATTLEFIELD, playerA, necromancer);
|
addCard(Zone.BATTLEFIELD, playerA, necromancer);
|
||||||
|
@ -257,7 +255,6 @@ public class DraugrNecromancerTest extends CardTestPlayerBase {
|
||||||
assertExileCount(playerB, birgi, 0);
|
assertExileCount(playerB, birgi, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testCastMDFCBackFromExileWithSnow() {
|
public void testCastMDFCBackFromExileWithSnow() {
|
||||||
addCard(Zone.BATTLEFIELD, playerA, necromancer);
|
addCard(Zone.BATTLEFIELD, playerA, necromancer);
|
||||||
|
|
|
@ -1,27 +1,30 @@
|
||||||
|
|
||||||
package mage.abilities.effects;
|
package mage.abilities.effects;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.constants.ManaType;
|
import mage.constants.ManaType;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.ManaPoolItem;
|
import mage.players.ManaPoolItem;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public interface AsThoughManaEffect extends AsThoughEffect {
|
public interface AsThoughManaEffect extends AsThoughEffect {
|
||||||
|
|
||||||
// return a mana type that can be used to pay a mana cost instead of the normally needed mana type
|
|
||||||
/**
|
/**
|
||||||
|
* Return a mana type that can be used to pay a mana cost instead of the normally needed mana type
|
||||||
|
* <p>
|
||||||
|
* Usage instructions:
|
||||||
|
* - on non applied effect: must return null (example: basic object applied, by you need to check a mana source too, see Draugr Necromancer);
|
||||||
|
* - on applied effect: must return compatible manaType with ManaPoolItem (e.g. return mana.getFirstAvailable)
|
||||||
*
|
*
|
||||||
* @param manaType type of mana with which the player wants to pay the cost
|
* @param manaType type of mana with which the player wants to pay the cost
|
||||||
* @param mana mana pool item to pay from the cost
|
* @param mana mana pool item to pay from the cost
|
||||||
* @param affectedControllerId
|
* @param affectedControllerId
|
||||||
* @param source
|
* @param source
|
||||||
* @param game
|
* @param game
|
||||||
* @return
|
* @return null on non applied effect
|
||||||
*/
|
*/
|
||||||
ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game);
|
ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game);
|
||||||
|
|
||||||
|
|
|
@ -612,6 +612,23 @@ public class ContinuousEffects implements Serializable {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fit paying mana type with current mana pool (if asThoughMana affected)
|
||||||
|
* <p>
|
||||||
|
* Example:
|
||||||
|
* - you need to pay {R} as cost
|
||||||
|
* - asThoughMana effect allows to use {G} as any color;
|
||||||
|
* - asThoughMana effect must change/fit paying mana type from {R} to {G}
|
||||||
|
* - after that you can pay {G} as cost
|
||||||
|
*
|
||||||
|
* @param manaType paying mana type
|
||||||
|
* @param mana checking pool item
|
||||||
|
* @param objectId paying ability's source object
|
||||||
|
* @param affectedAbility paying ability
|
||||||
|
* @param controllerId controller who pay
|
||||||
|
* @param game
|
||||||
|
* @return corrected paying mana type (same if no asThough effects and different on applied asThough effect)
|
||||||
|
*/
|
||||||
public ManaType asThoughMana(ManaType manaType, ManaPoolItem mana, UUID objectId, Ability affectedAbility, UUID controllerId, Game game) {
|
public ManaType asThoughMana(ManaType manaType, ManaPoolItem mana, UUID objectId, Ability affectedAbility, UUID controllerId, Game game) {
|
||||||
// First check existing only effects
|
// First check existing only effects
|
||||||
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(AsThoughEffectType.SPEND_ONLY_MANA, game);
|
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(AsThoughEffectType.SPEND_ONLY_MANA, game);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import mage.abilities.*;
|
||||||
import mage.abilities.costs.mana.ManaCost;
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
import mage.abilities.costs.mana.ManaCosts;
|
import mage.abilities.costs.mana.ManaCosts;
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
|
import mage.counters.Counter;
|
||||||
import mage.counters.Counters;
|
import mage.counters.Counters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.GameState;
|
import mage.game.GameState;
|
||||||
|
@ -123,6 +124,16 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
|
||||||
return state.getCardState(leftHalfCard.getId()).getCounters();
|
return state.getCardState(leftHalfCard.getId()).getCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addCounters(Counter counter, UUID playerAddingCounters, Ability source, Game game, List<UUID> appliedEffects, boolean isEffect) {
|
||||||
|
return leftHalfCard.addCounters(counter, playerAddingCounters, source, game, appliedEffects, isEffect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCounters(String name, int amount, Ability source, Game game) {
|
||||||
|
leftHalfCard.removeCounters(name, amount, source, game);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
|
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
|
||||||
switch (ability.getSpellAbilityType()) {
|
switch (ability.getSpellAbilityType()) {
|
||||||
|
|
|
@ -2985,39 +2985,25 @@ public abstract class GameImpl implements Game, Serializable {
|
||||||
if (object instanceof Permanent || object instanceof StackObject) {
|
if (object instanceof Permanent || object instanceof StackObject) {
|
||||||
MageObject copy = object.copy();
|
MageObject copy = object.copy();
|
||||||
|
|
||||||
Map<UUID, MageObject> lkiMap = lki.get(zone);
|
Map<UUID, MageObject> lkiMap = lki.computeIfAbsent(zone, k -> new HashMap<>());
|
||||||
if (lkiMap != null) {
|
lkiMap.put(objectId, copy);
|
||||||
lkiMap.put(objectId, copy);
|
|
||||||
} else {
|
|
||||||
Map<UUID, MageObject> newMap = new HashMap<>();
|
|
||||||
newMap.put(objectId, copy);
|
|
||||||
lki.put(zone, newMap);
|
|
||||||
}
|
|
||||||
// remembers if a object was in a zone during the resolution of an effect
|
// remembers if a object was in a zone during the resolution of an effect
|
||||||
// e.g. Wrath destroys all and you the question is is the replacement effect to apply because the source was also moved by the same effect
|
// e.g. Wrath destroys all and you the question is is the replacement effect to apply because the source was also moved by the same effect
|
||||||
// because it happens all at the same time the replacement effect has still to be applied
|
// because it happens all at the same time the replacement effect has still to be applied
|
||||||
Set<UUID> idSet = shortLivingLKI.computeIfAbsent(zone, k -> new HashSet<>());
|
Set<UUID> idSet = shortLivingLKI.computeIfAbsent(zone, k -> new HashSet<>());
|
||||||
idSet.add(objectId);
|
idSet.add(objectId);
|
||||||
if (object instanceof Permanent) {
|
if (object instanceof Permanent) {
|
||||||
Map<Integer, MageObject> lkiExtendedMap = lkiExtended.get(objectId);
|
Map<Integer, MageObject> lkiExtendedMap = lkiExtended.computeIfAbsent(objectId, k -> new HashMap<>());
|
||||||
if (lkiExtendedMap != null) {
|
lkiExtendedMap.put(object.getZoneChangeCounter(this), copy);
|
||||||
lkiExtendedMap.put(object.getZoneChangeCounter(this), copy);
|
|
||||||
} else {
|
|
||||||
lkiExtendedMap = new HashMap<>();
|
|
||||||
lkiExtendedMap.put(object.getZoneChangeCounter(this), copy);
|
|
||||||
lkiExtended.put(objectId, lkiExtendedMap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (zone.isPublicZone()) {
|
} else if (zone.isPublicZone()) {
|
||||||
// Remember card state in this public zone (mainly removed/gained abilities)
|
// Remember card state in this public zone (mainly removed/gained abilities)
|
||||||
Map<UUID, CardState> lkiMap = lkiCardState.get(zone);
|
// Must save all card parts (mdf, split)
|
||||||
if (lkiMap != null) {
|
CardUtil.getObjectParts(object).forEach(partId -> {
|
||||||
lkiMap.put(objectId, getState().getCardState(objectId));
|
Map<UUID, CardState> lkiMap = lkiCardState.computeIfAbsent(zone, k -> new HashMap<>());
|
||||||
} else {
|
lkiMap.put(partId, getState().getCardState(partId).copy());
|
||||||
Map<UUID, CardState> newMap = new HashMap<>();
|
});
|
||||||
newMap.put(objectId, getState().getCardState(objectId).copy());
|
|
||||||
lkiCardState.put(zone, newMap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -339,15 +339,28 @@ public class ManaPool implements Serializable {
|
||||||
Mana mana = manaToAdd.copy();
|
Mana mana = manaToAdd.copy();
|
||||||
if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source, playerId, mana))) {
|
if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source, playerId, mana))) {
|
||||||
if (mana instanceof ConditionalMana) {
|
if (mana instanceof ConditionalMana) {
|
||||||
ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game),
|
ConditionalMana conditionalMana = (ConditionalMana) mana;
|
||||||
((ConditionalMana) mana).getManaProducerOriginalId() != null
|
ManaPoolItem item = new ManaPoolItem(
|
||||||
? ((ConditionalMana) mana).getManaProducerOriginalId() : source.getOriginalId());
|
conditionalMana,
|
||||||
|
source.getSourceObject(game),
|
||||||
|
conditionalMana.getManaProducerOriginalId() != null ? conditionalMana.getManaProducerOriginalId() : source.getOriginalId()
|
||||||
|
);
|
||||||
if (emptyOnTurnsEnd) {
|
if (emptyOnTurnsEnd) {
|
||||||
item.setDuration(Duration.EndOfTurn);
|
item.setDuration(Duration.EndOfTurn);
|
||||||
}
|
}
|
||||||
this.manaItems.add(item);
|
this.manaItems.add(item);
|
||||||
} else {
|
} else {
|
||||||
ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getGeneric() + mana.getColorless(), source.getSourceObject(game), source.getOriginalId(), mana.getFlag());
|
ManaPoolItem item = new ManaPoolItem(
|
||||||
|
mana.getRed(),
|
||||||
|
mana.getGreen(),
|
||||||
|
mana.getBlue(),
|
||||||
|
mana.getWhite(),
|
||||||
|
mana.getBlack(),
|
||||||
|
mana.getGeneric() + mana.getColorless(),
|
||||||
|
source.getSourceObject(game),
|
||||||
|
source.getOriginalId(),
|
||||||
|
mana.getFlag()
|
||||||
|
);
|
||||||
if (emptyOnTurnsEnd) {
|
if (emptyOnTurnsEnd) {
|
||||||
item.setDuration(Duration.EndOfTurn);
|
item.setDuration(Duration.EndOfTurn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import mage.cards.ModalDoubleFacesCard;
|
||||||
import mage.cards.SplitCard;
|
import mage.cards.SplitCard;
|
||||||
import mage.choices.Choice;
|
import mage.choices.Choice;
|
||||||
import mage.constants.ColoredManaSymbol;
|
import mage.constants.ColoredManaSymbol;
|
||||||
|
import mage.constants.ManaType;
|
||||||
import mage.filter.FilterMana;
|
import mage.filter.FilterMana;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
@ -699,6 +700,23 @@ public final class ManaUtil {
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all used mana types in mana cost (wubrg + colorless)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<ManaType> getManaTypesInCost(ManaCost cost) {
|
||||||
|
List<ManaType> res = new ArrayList<>();
|
||||||
|
for (Mana mana : cost.getManaOptions()) {
|
||||||
|
if (mana.getWhite() > 0) res.add(ManaType.WHITE);
|
||||||
|
if (mana.getBlue() > 0) res.add(ManaType.BLUE);
|
||||||
|
if (mana.getBlack() > 0) res.add(ManaType.BLACK);
|
||||||
|
if (mana.getRed() > 0) res.add(ManaType.RED);
|
||||||
|
if (mana.getGreen() > 0) res.add(ManaType.GREEN);
|
||||||
|
if (mana.getColorless() > 0 || mana.getGeneric() > 0 || mana.getAny() > 0) res.add(ManaType.COLORLESS);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue