Merge branch '1495-morph-x-costs-x-is-not-being-remembered' into 'master'

Resolve "Morph X costs - X is not being remembered"

Closes #1496 and #1495

See merge request core-developers/forge!2969
This commit is contained in:
Michael Kamensky
2020-07-07 09:58:08 +00:00
19 changed files with 183 additions and 30 deletions

View File

@@ -174,7 +174,7 @@ public class SetStateAi extends SpellAbilityAi {
if (!card.isFaceDown()) {
transformed.turnFaceDown(true);
} else {
transformed.turnFaceUp(false, false);
transformed.forceTurnFaceUp();
}
transformed.updateStateForView();
return compareCards(card, transformed, ai, ph);

View File

@@ -202,7 +202,7 @@ public class GameAction {
// all sort of funky shenanigans may happen later (e.g. their ETB replacement effects are set
// up on the wrong card state etc.).
if (wasFacedown && (fromBattlefield || (toHand && zoneFrom.is(ZoneType.Exile)))) {
c.turnFaceUp(false, false);
c.forceTurnFaceUp();
}
if (!c.isToken()) {
@@ -264,7 +264,7 @@ public class GameAction {
if (repres != ReplacementResult.NotReplaced) {
// reset failed manifested Cards back to original
if (c.isManifested() && !c.isInZone(ZoneType.Battlefield)) {
c.turnFaceUp(false, false);
c.forceTurnFaceUp();
}
copied.getOwner().removeInboundToken(copied);
@@ -431,7 +431,7 @@ public class GameAction {
// rule 504.6: reveal a face-down card leaving the stack
if (zoneFrom != null && zoneTo != null && zoneFrom.is(ZoneType.Stack) && !zoneTo.is(ZoneType.Battlefield) && wasFacedown) {
Card revealLKI = CardUtil.getLKICopy(c);
revealLKI.turnFaceUp(true, false);
revealLKI.forceTurnFaceUp();
reveal(new CardCollection(revealLKI), revealLKI.getOwner(), true, "Face-down card moves from the stack: ");
}
@@ -462,7 +462,7 @@ public class GameAction {
// Reveal if face-down
if (wasFacedown) {
Card revealLKI = CardUtil.getLKICopy(c);
revealLKI.turnFaceUp(true, false);
revealLKI.forceTurnFaceUp();
reveal(new CardCollection(revealLKI), revealLKI.getOwner(), true, "Face-down card leaves the battlefield: ");

View File

@@ -1650,6 +1650,12 @@ public class AbilityUtils {
return CardFactoryUtil.doXMath(0, expr, c);
}
return CardFactoryUtil.doXMath(cycleSA.getXManaCostPaid(), expr, c);
} else if (TriggerType.TurnFaceUp.equals(t.getMode())) {
SpellAbility turnupSA = (SpellAbility) sa.getTriggeringObject(AbilityKey.Cause);
if (turnupSA == null || turnupSA.getXManaCostPaid() == null) {
return CardFactoryUtil.doXMath(0, expr, c);
}
return CardFactoryUtil.doXMath(turnupSA.getXManaCostPaid(), expr, c);
}
}

View File

@@ -483,7 +483,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
if (sa.hasParam("Transformed")) {
if (tgtC.isDoubleFaced()) {
tgtC.changeCardState("Transform", null);
tgtC.changeCardState("Transform", null, sa);
} else {
// If it can't Transform, don't change zones.
continue;
@@ -1031,7 +1031,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
if (sa.hasParam("Transformed")) {
if (c.isDoubleFaced()) {
c.changeCardState("Transform", null);
c.changeCardState("Transform", null, sa);
} else {
// If it can't Transform, don't change zones.
continue;

View File

@@ -19,8 +19,6 @@ import forge.util.Aggregates;
import forge.util.TextUtil;
import forge.util.Localizer;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -294,8 +292,7 @@ public class DiscardEffect extends SpellAbilityEffect {
continue; // for loop over players
if (sa.hasParam("RevealNumber")) {
String amountString = sa.getParam("RevealNumber");
int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : CardFactoryUtil.xCount(source, source.getSVar(amountString));
int amount = AbilityUtils.calculateAmount(source, sa.getParam("RevealNumber"), sa);
dPHand = p.getController().chooseCardsToRevealFromHand(amount, amount, dPHand);
}

View File

@@ -153,7 +153,7 @@ public class PlayEffect extends SpellAbilityEffect {
final boolean wasFaceDown;
if (tgtCard.isFaceDown()) {
tgtCard.turnFaceUp(false, false);
tgtCard.forceTurnFaceUp();
wasFaceDown = true;
} else {
wasFaceDown = false;

View File

@@ -76,7 +76,7 @@ public class SetStateEffect extends SpellAbilityEffect {
if ("TurnFace".equals(mode) && tgt.isFaceDown() && tgt.isInZone(ZoneType.Battlefield)
&& !tgt.getState(CardStateName.Original).getType().isPermanent()) {
Card lki = CardUtil.getLKICopy(tgt);
lki.turnFaceUp(true, false);
lki.forceTurnFaceUp();
game.getAction().reveal(new CardCollection(lki), lki.getOwner(), true, Localizer.getInstance().getMessage("lblFaceDownCardCantTurnFaceUp"));
continue;
@@ -106,11 +106,11 @@ public class SetStateEffect extends SpellAbilityEffect {
boolean hasTransformed = false;
if (morphUp) {
hasTransformed = tgt.turnFaceUp();
hasTransformed = tgt.turnFaceUp(sa);
} else if (manifestUp) {
hasTransformed = tgt.turnFaceUp(true, true);
hasTransformed = tgt.turnFaceUp(true, true, sa);
} else {
hasTransformed = tgt.changeCardState(mode, sa.getParam("NewState"));
hasTransformed = tgt.changeCardState(mode, sa.getParam("NewState"), sa);
}
if ( hasTransformed ) {
if (morphUp) {

View File

@@ -549,7 +549,7 @@ public class Card extends GameEntity implements Comparable<Card> {
currentState.getView().updateType(currentState);
}
public boolean changeCardState(final String mode, final String customState) {
public boolean changeCardState(final String mode, final String customState, final SpellAbility cause) {
if (mode == null)
return changeToState(CardStateName.smartValueOf(customState));
@@ -597,7 +597,7 @@ public class Card extends GameEntity implements Comparable<Card> {
if (oldState == CardStateName.Original || oldState == CardStateName.Flipped) {
return turnFaceDown();
} else if (isFaceDown()) {
return turnFaceUp();
return turnFaceUp(cause);
}
} else if (mode.equals("Meld") && isMeldable()) {
return changeToState(CardStateName.Meld);
@@ -656,11 +656,11 @@ public class Card extends GameEntity implements Comparable<Card> {
return setState(CardStateName.FaceDown, false);
}
public boolean turnFaceUp() {
return turnFaceUp(false, true);
public boolean turnFaceUp(SpellAbility cause) {
return turnFaceUp(false, true, cause);
}
public boolean turnFaceUp(boolean manifestPaid, boolean runTriggers) {
public boolean turnFaceUp(boolean manifestPaid, boolean runTriggers, SpellAbility cause) {
if (isFaceDown()) {
if (manifestPaid && isManifested() && !getRules().getType().isCreature()) {
// If we've manifested a non-creature and we're demanifesting disallow it
@@ -688,8 +688,11 @@ public class Card extends GameEntity implements Comparable<Card> {
getGame().getReplacementHandler().run(ReplacementType.TurnFaceUp, AbilityKey.mapFromAffected(this));
// Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(this);
runParams.put(AbilityKey.Cause, cause);
getGame().getTriggerHandler().registerActiveTrigger(this, false);
getGame().getTriggerHandler().runTrigger(TriggerType.TurnFaceUp, AbilityKey.mapFromCard(this), false);
getGame().getTriggerHandler().runTrigger(TriggerType.TurnFaceUp, runParams, false);
}
return result;
}
@@ -6102,7 +6105,7 @@ public class Card extends GameEntity implements Comparable<Card> {
if (isFaceDown()) {
lkicheck = true;
source = CardUtil.getLKICopy(source);
source.turnFaceUp(false, false);
source.forceTurnFaceUp();
}
if (lkicheck) {
@@ -6315,7 +6318,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public void forceTurnFaceUp() {
getGame().getTriggerHandler().suppressMode(TriggerType.TurnFaceUp);
turnFaceUp(false, false);
turnFaceUp(false, false, null);
getGame().getTriggerHandler().clearSuppression(TriggerType.TurnFaceUp);
}

View File

@@ -1772,7 +1772,7 @@ public class Player extends GameEntity implements Comparable<Player> {
public final void playLandNoCheck(final Card land) {
land.setController(this, 0);
if (land.isFaceDown()) {
land.turnFaceUp();
land.turnFaceUp(null);
}
final Card c = game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null);
game.updateLastStateForCard(c);

View File

@@ -209,7 +209,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
source = CardUtil.getLKICopy(source);
}
source.turnFaceUp(false, false);
source.forceTurnFaceUp();
lkicheck = true;
}

View File

@@ -65,7 +65,7 @@ public class TriggerTurnFaceUp extends Trigger {
/** {@inheritDoc} */
@Override
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card);
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card, AbilityKey.Cause);
}
@Override

View File

@@ -242,7 +242,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
if (spell.isCastFaceDown()) {
source.turnFaceDown();
} else if (source.isFaceDown()) {
source.turnFaceUp();
source.turnFaceUp(null);
}
}

View File

@@ -0,0 +1,29 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Basri, Devoted Paladin
Description=Basri is a new planeswalker from the Egyptian themed plane Amonkhet. His deck plays a bunch of small creatures and buffs them with +1/+1 counters.
Deck Type=constructed
Set=M21
Image=basri_devoted_paladin.jpg
[Main]
4 Adherent of Hope|M21
3 Anointed Chorister|M21
2 Aven Gagglemaster|M21
2 Banishing Light|THB
3 Basri's Acolyte|M21
2 Basri's Aegis|M21
1 Basri's Lieutenant|M21
1 Basri, Devoted Paladin+|M21
3 Fight as One|IKO
4 Makeshift Battalion|WAR
1 Perimeter Sergeant|IKO
25 Plains|M21
3 Sigiled Contender|M21
1 Speaker of the Heavens|M21
2 Swift Response|M21
3 Tempered Veteran|M21
[Sideboard]

View File

@@ -0,0 +1,30 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Chandra, Flame's Catalyst
Description=As you can expect from Chandra, theres a lot of burning going on in this deck. Burn your opponents, burn their creatures, and make you aggressive creatures grow while youre at it.
Deck Type=constructed
Set=M21
Image=chandra_flames_catalyst.jpg
[Main]
2 Blisterspit Gremlin|IKO
2 Chandra's Firemaw|M21
1 Chandra's Incinerator|M21
3 Chandra's Pyreling|M21
1 Chandra, Flame's Catalyst+|M21
1 Double Vision|M21
3 Heartfire Immolator|M21
2 Infuriate|THB
3 Keral Keep Disciples|M21
25 Mountain|M21
2 Pyroceratops|IKO
3 Shock|M21
2 Slaying Fire|ELD
2 Spellgorger Weird|M21
4 Storm Caller|M21
2 Sure Strike|M21
2 Unleash Fury|M21
[Sideboard]

View File

@@ -0,0 +1,29 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Garruk, Savage Herald
Description=Be Garruk. Play big creatures. Then play bigger creatures. Great deck for everyone who likes… you guessed it big creatures.
Deck Type=constructed
Set=M21
Image=garruk_savage_herald.jpg
[Main]
3 Almighty Brushwagg|IKO
3 Drowsing Tyrannodon|M21
25 Forest|M21
1 Garruk's Harbinger|M21
2 Garruk's Uprising|M21
2 Garruk's Warsteed|M21
1 Garruk, Savage Herald+|M21
2 Ilysian Caryatid|THB
3 Nessian Hornbeetle|THB
2 Ornery Dilophosaur|M21
3 Predatory Wurm|M21
3 Pridemalkin|M21
1 Primal Might|M21
2 Ram Through|IKO
3 Titanic Growth|M20
4 Wildwood Patrol|M21
[Sideboard]

View File

@@ -0,0 +1,29 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Liliana, Death Mage
Description=The black deck cares about the graveyard. When your creatures die, you get all sorts of benefits. Then you can get them back from the grave with Liliana or with some other cards.
Deck Type=constructed
Set=M21
Image=liliana_death_mage.jpg
[Main]
2 Alchemist's Gift|M21
2 Bushmeat Poacher|IKO
1 Demonic Embrace|M21
3 Durable Coilbug|IKO
2 Goremand|M21
3 Grasp of Darkness|OGW
3 Grim Physician|THB
2 Liliana's Scorn|M21
3 Liliana's Scrounger|M21
1 Liliana's Standard Bearer|M21
1 Liliana, Death Mage+|M21
2 Lurking Deadeye|IKO
4 Masked Blackguard|M21
4 Spirit of Malevolence|M21
25 Swamp|M21
2 Unlikely Aid|IKO
[Sideboard]

View File

@@ -0,0 +1,30 @@
[shop]
WinsToUnlock=0
Credits=1200
MinDifficulty=0
MaxDifficulty=5
[metadata]
Name=Teferi, Timeless Voyager
Description=Teferis deck does what blue decks do best it draws tons of cards! Deck also contains a bunch of cards that synergize with card draw.
Deck Type=constructed
Set=M21
Image=teferi_timeless_voyager.jpg
[Main]
2 Charmed Sleep|ELD
3 Faerie Vandal|ELD
4 Frantic Inventory|M21
2 Gust of Wind|IKO
3 Historian of Zhalfir|M21
25 Island|M21
2 Jeskai Elder|KTK
2 Mantle of Tides|ELD
4 Mystic Skyfish|M21
3 Opt|ELD
2 Shipwreck Dowser|M21
1 Stern Dismissal|THB
1 Stormwing Entity|M21
1 Teferi's Ageless Insight|M21
2 Teferi's Wavecaster|M21
1 Teferi, Timeless Voyager|M21
2 Tome Anima|M21
[Sideboard]

View File

@@ -82,7 +82,7 @@ public class HumanPlay {
}
if (flippedToCast && !castFaceDown) {
source.turnFaceUp(false, false);
source.forceTurnFaceUp();
}
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {

View File

@@ -103,7 +103,7 @@ public class HumanPlaySpellAbility {
// This is should happen earlier, before the Modal spell is chosen
// Turn face-down card face up (except case of morph spell)
if (ability.isSpell() && !ability.isCastFaceDown() && fromState == CardStateName.FaceDown) {
c.turnFaceUp();
c.turnFaceUp(null);
}
ability.setHostCard(game.getAction().moveToStack(c, ability));
}