Skip to content

Commit

Permalink
Merseine fixes (#4579)
Browse files Browse the repository at this point in the history
* Merseine fixes

* Clean up

* Fix NPE with Dire Blunderbuss
  • Loading branch information
tool4ever authored Jan 23, 2024
1 parent c22bcba commit d93d9b9
Show file tree
Hide file tree
Showing 17 changed files with 62 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ public Card chooseCardToHiddenOriginChangeZone(ZoneType destination, List<ZoneTy
public CardCollectionView chooseSacrificeType(String type, SpellAbility ability, final boolean effect, int amount, final CardCollectionView exclude) {
if (amount == 1) {
Card source = ability.getHostCard();
CardCollection cardList = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, null);
CardCollection cardList = CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, ability);
cardList = CardLists.filter(cardList, CardPredicates.canBeSacrificedBy(ability, effect));
if (cardList.size() >= 2) {
if (interceptor != null) {
Expand Down
2 changes: 1 addition & 1 deletion forge-game/src/main/java/forge/game/CardTraitBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
* Keys that should not changed
*/
private static final ImmutableList<String> noChangeKeys = ImmutableList.<String>builder()
.add("TokenScript", "LegacyImage", "TokenImage", "NewName", "ChooseFromList")
.add("TokenScript", "TokenImage", "NewName", "ChooseFromList")
.add("AddAbility").build();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,11 +718,7 @@ else if (calcX[0].startsWith("ReplaceCount")) {
list = sa.getRootAbility().getPaidList("Sacrificed", true);
}
else if (calcX[0].startsWith("Discarded")) {
final SpellAbility root = sa.getRootAbility();
list = root.getPaidList("Discarded", true);
if (null == list && root.isTrigger()) {
list = root.getHostCard().getSpellPermanent().getPaidList("Discarded", true);
}
list = sa.getRootAbility().getPaidList("Discarded", true);
}
else if (calcX[0].startsWith("Exiled")) {
list = sa.getRootAbility().getPaidList("Exiled", true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public void resolve(SpellAbility sa) {
if (pstr.contains("Controller")) {
placerPerCard = true;
} else {
placer = AbilityUtils.getDefinedPlayers(host, pstr, sa).get(0);
placer = AbilityUtils.getDefinedPlayers(host, pstr, sa).get(0);
}
}

Expand Down
27 changes: 12 additions & 15 deletions forge-game/src/main/java/forge/game/cost/Cost.java
Original file line number Diff line number Diff line change
Expand Up @@ -938,26 +938,23 @@ public Cost add(Cost cost1) {
public Cost add(Cost cost1, boolean mergeAdditional) {
return add(cost1, mergeAdditional, null);
}
public Cost add(Cost cost1, boolean mergeAdditional, final SpellAbility sa) {
CostPartMana costPart2 = this.getCostMana();
public Cost add(Cost cost, boolean mergeAdditional, final SpellAbility sa) {
CostPartMana mPartOld = this.getCostMana();
List<CostPart> toRemove = Lists.newArrayList();
for (final CostPart part : cost1.getCostParts()) {
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostPartMana && ((CostPartMana) part).getMana().isZero()) {
continue; // do not add Zero Mana
} else if (part instanceof CostPartMana && costPart2 != null) {
} else if (part instanceof CostPartMana && mPartOld != null) {
CostPartMana mPart = (CostPartMana) part;
ManaCostBeingPaid oldManaCost = new ManaCostBeingPaid(mPart.getMana());
oldManaCost.addManaCost(costPart2.getMana());
costParts.remove(costPart2);
int xMin = mPart.getXMin();
if (costPart2.getXMin() > xMin) xMin = costPart2.getXMin();
if (mPart.isExiledCreatureCost() || mPart.isEnchantedCreatureCost() || xMin > 0) {
// FIXME: something was amiss when trying to add the cost since the mana cost is either \EnchantedCost or \Exiled but the
// restriction no longer marks it as such. Therefore, we need to explicitly copy the ExiledCreatureCost/EnchantedCreatureCost
// to make cards like Merseine or Back from the Brink work.
costParts.add(0, new CostPartMana(oldManaCost.toManaCost(), mPart.isExiledCreatureCost(), mPart.isEnchantedCreatureCost(), xMin));
ManaCostBeingPaid manaCost = new ManaCostBeingPaid(mPart.getMana());
costParts.remove(mPartOld);
int xMin = Math.max(mPart.getXMin(), mPartOld.getXMin());
manaCost.addManaCost(mPartOld.getMana());
if (mPartOld.isExiledCreatureCost() || mPartOld.isEnchantedCreatureCost() || xMin > 0) {
// need to explicitly copy the ExiledCreatureCost/EnchantedCreatureCost
costParts.add(0, new CostPartMana(manaCost.toManaCost(), mPartOld.isExiledCreatureCost(), mPartOld.isEnchantedCreatureCost(), xMin));
} else {
costParts.add(0, new CostPartMana(oldManaCost.toManaCost(), null));
costParts.add(0, new CostPartMana(manaCost.toManaCost(), null));
}
} else if (part instanceof CostPutCounter || (mergeAdditional && // below usually not desired because they're from different causes
(part instanceof CostDiscard || part instanceof CostDraw ||
Expand Down
4 changes: 2 additions & 2 deletions forge-game/src/main/java/forge/game/cost/CostAdjustment.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public static Cost adjust(final Cost cost, final SpellAbility sa) {
result.add(new Cost(ManaCost.get(n), false));
}
}
} // isSpell
}

CardCollection cardsOnBattlefield = new CardCollection(game.getCardsIn(ZoneType.Battlefield));
cardsOnBattlefield.addAll(game.getCardsIn(ZoneType.Stack));
Expand Down Expand Up @@ -223,7 +223,7 @@ else if (stAb.checkMode("SetCost")) {
// need to reduce generic extra because of 2 hybrid mana
cost.decreaseGenericMana(sumGeneric);

if (sa.isSpell() && !sa.getPipsToReduce().isEmpty()) {
if (sa.isSpell()) {
for (String pip : sa.getPipsToReduce()) {
cost.decreaseShard(ManaCostShard.parseNonGeneric(pip), 1);
}
Expand Down
18 changes: 14 additions & 4 deletions forge-game/src/main/java/forge/game/cost/CostPartMana.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,22 @@ public <T> T accept(ICostVisitor<T> visitor) {

public ManaCost getManaCostFor(SpellAbility sa) {
if (isExiledCreatureCost() && sa.getPaidList(CostExile.HashLKIListKey, true) != null && !sa.getPaidList(CostExile.HashLKIListKey, true).isEmpty()) {
// back from the brink
return sa.getPaidList(CostExile.HashLKIListKey, true).get(0).getManaCost();
ManaCost mod = sa.getPaidList(CostExile.HashLKIListKey, true).get(0).getManaCost();
if (mod.isNoCost()) {
return mod;
}
ManaCostBeingPaid manaCostNew = new ManaCostBeingPaid(getMana());
manaCostNew.addManaCost(mod);
return manaCostNew.toManaCost();
}
if (isEnchantedCreatureCost() && sa.getHostCard().isEnchantingCard()) {
// TODO human can still activate on TDFC backside
return sa.getHostCard().getEnchantingCard().getManaCost();
ManaCost mod = sa.getHostCard().getEnchantingCard().getManaCost();
if (mod.isNoCost()) {
return mod;
}
ManaCostBeingPaid manaCostNew = new ManaCostBeingPaid(getMana());
manaCostNew.addManaCost(mod);
return manaCostNew.toManaCost();
}
if (isCostPayAnyNumberOfTimes) {
int timesToPay = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getSVar("NumTimes"), sa);
Expand Down
2 changes: 1 addition & 1 deletion forge-game/src/main/java/forge/game/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -1724,7 +1724,7 @@ public final boolean canPlayLand(final Card land, final boolean ignoreZoneAndTim
if (!game.getPhaseHandler().isPlayerTurn(this)) {
return false;
}
if (!canCastSorcery() && !landSa.withFlash(land, this)) {
if (!canCastSorcery() && (landSa == null || !landSa.withFlash(land, this))) {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public AbilityActivated(final Card sourceCard, final Cost abCost, final TargetRe
/** {@inheritDoc} */
@Override
public boolean canPlay() {
// CR 118.6 cost is unpayable
if (getPayCosts().hasManaCost() && getPayCosts().getCostMana().getManaCostFor(this).isNoCost()) {
return false;
}

Player player = getActivatingPlayer();
if (player == null) {
player = this.getHostCard().getController();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,6 @@ public final boolean areMet(final SpellAbility sa) {
if (!Expressions.compare(svarValue, this.getsVarOperator(), operandValue) && !secondCheck) {
return false;
}

}

return true;
Expand Down
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/b/back_from_the_brink.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Name:Back from the Brink
ManaCost:4 U U
Types:Enchantment
A:AB$ CopyPermanent | Cost$ ExileFromGrave<1/Creature> Mana<20\Exiled> | CostDesc$ Exile a creature card from your graveyard and pay its mana cost: | Defined$ Exiled | SorcerySpeed$ True | SpellDescription$ Create a token that's a copy of that card. Activate only as a sorcery.
A:AB$ CopyPermanent | Cost$ ExileFromGrave<1/Creature> Mana<0\Exiled> | CostDesc$ Exile a creature card from your graveyard and pay its mana cost: | Defined$ Exiled | SorcerySpeed$ True | SpellDescription$ Create a token that's a copy of that card. Activate only as a sorcery.
SVar:NonStackingEffect:True
Oracle:Exile a creature card from your graveyard and pay its mana cost: Create a token that's a copy of that card. Activate only as a sorcery.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/c/consecrate_consume.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ALTERNATE
Name:Consume
ManaCost:2 W B
Types:Sorcery
A:SP$ Sacrifice | Cost$ 2 W B | ValidTgts$ Player | SacValid$ Creature.greatestPowerControlledByTargered | SubAbility$ DBGainLife | SacMessage$ the creature with the highest power | RememberSacrificed$ True | SpellDescription$ Target player sacrifices a creature with the greatest power among creatures they control. You gain life equal to its power.
A:SP$ Sacrifice | Cost$ 2 W B | ValidTgts$ Player | SacValid$ Creature.greatestPowerControlledByTargeted | SubAbility$ DBGainLife | SacMessage$ the creature with the highest power | RememberSacrificed$ True | SpellDescription$ Target player sacrifices a creature with the greatest power among creatures they control. You gain life equal to its power.
SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:RememberedLKI$CardPower
Expand Down
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/l/leafkin_avenger.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Name:Leafkin Avenger
ManaCost:2 R G
Types:Creature Elemental Druid
PT:4/3
A:AB$ Mana | Cost$ T | Produced$ G | Amount$ X | SpellDescription$ Add {G} for each Creature you control with power 4 or greater.
A:AB$ Mana | Cost$ T | Produced$ G | Amount$ X | SpellDescription$ Add {G} for each creature you control with power 4 or greater.
SVar:X:Count$Valid Creature.YouCtrl+powerGE4
A:AB$ DealDamage | Cost$ 7 R | ValidTgts$ Player,Planeswalker | TgtPrompt$ Select target player or planeswalker | NumDmg$ Y | SpellDescription$ CARDNAME deals damage equal to its power to target player or planeswalker.
SVar:Y:Count$CardPower
Expand Down
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/m/merseine.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ K:Enchant creature
A:SP$ Attach | Cost$ 2 U U | ValidTgts$ Creature | AILogic$ KeepTapped | AITgts$ Card.cmcGE2
K:etbCounter:NET:3
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | CheckSVar$ X | Description$ Enchanted creature doesn't untap during its controller's untap step if CARDNAME has a net counter on it.
A:AB$ RemoveCounter | Activator$ Player.EnchantedController | Cost$ Mana<1\EnchantedCost> | CostDesc$ Pay enchanted creature's mana cost: | CounterType$ NET | CounterNum$ 1 | SpellDescription$ Remove a net counter from CARDNAME. Only the controller of the enchanted creature may activate this ability.
A:AB$ RemoveCounter | Activator$ Player.EnchantedController | Cost$ Mana<0\EnchantedCost> | CostDesc$ Pay enchanted creature's mana cost: | CounterType$ NET | CounterNum$ 1 | SpellDescription$ Remove a net counter from CARDNAME. Only the controller of the enchanted creature may activate this ability.
SVar:X:Count$CardCounters.NET
Oracle:Enchant creature\nMerseine enters the battlefield with three net counters on it.\nEnchanted creature doesn't untap during its controller's untap step if Merseine has a net counter on it.\nPay enchanted creature's mana cost: Remove a net counter from Merseine. Only the controller of the enchanted creature may activate this ability.
19 changes: 11 additions & 8 deletions forge-gui/res/cardsfolder/t/tegwyll_duke_of_splendor.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
Name:Tegwyll's Scouring
ManaCost:4 B B
Types:Sorcery
K:MayFlashCost:tapXType<3/Creature.withFlying/creatures with flying>
A:SP$ DestroyAll | ValidCards$ Creature | SubAbility$ DBToken | SpellDescription$ Destroy all creatures. Create three 1/1 black Faerie Rogue creature tokens with flying.
SVar:DBToken:DB$ Token | TokenAmount$ 3 | TokenScript$ b_1_1_faerie_rogue_flying | TokenOwner$ You
Name:Tegwyll, Duke of Splendor
ManaCost:1 U B
Types:Legendary Creature Faerie Noble
PT:2/3
K:Flying
K:Deathtouch
S:Mode$ Continuous | Affected$ Creature.Faerie+Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Faerie creatures you control get +1/+1.
T:Mode$ ChangesZone | ValidCard$ Faerie.Other+YouCtrl | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever another Faerie you control dies, you draw a card and you lose 1 life.
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBLoseLife
SVar:DBLoseLife:DB$ LoseLife | Defined$ You | LifeAmount$ 1
DeckHints:Type$Faerie
DeckHas:Ability$Token & Type$Faerie
Oracle:You may cast Tegwyll's Scouring as though it had flash by tapping three untapped creatures you control with flying in addition to paying its other costs.\nDestroy all creatures. Create three 1/1 black Faerie Rogue creature tokens with flying.
Oracle:Flying, deathtouch\nOther Faeries you control get +1/+1.\nWhenever another Faerie you control dies, you draw a card and you lose 1 life.
19 changes: 8 additions & 11 deletions forge-gui/res/cardsfolder/t/tegwylls_scouring.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
Name:Tegwyll, Duke of Splendor
ManaCost:1 U B
Types:Legendary Creature Faerie Noble
PT:2/3
K:Flying
K:Deathtouch
S:Mode$ Continuous | Affected$ Creature.Faerie+Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Faerie creatures you control get +1/+1.
T:Mode$ ChangesZone | ValidCard$ Faerie.Other+YouCtrl | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever another Faerie you control dies, you draw a card and you lose 1 life.
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBLoseLife
SVar:DBLoseLife:DB$ LoseLife | Defined$ You | LifeAmount$ 1
Name:Tegwyll's Scouring
ManaCost:4 B B
Types:Sorcery
K:MayFlashCost:tapXType<3/Creature.withFlying/creatures with flying>
A:SP$ DestroyAll | ValidCards$ Creature | SubAbility$ DBToken | SpellDescription$ Destroy all creatures. Create three 1/1 black Faerie Rogue creature tokens with flying.
SVar:DBToken:DB$ Token | TokenAmount$ 3 | TokenScript$ b_1_1_faerie_rogue_flying | TokenOwner$ You
DeckHints:Type$Faerie
Oracle:Flying, deathtouch\nOther Faeries you control get +1/+1.\nWhenever another Faerie you control dies, you draw a card and you lose 1 life.
DeckHas:Ability$Token & Type$Faerie
Oracle:You may cast Tegwyll's Scouring as though it had flash by tapping three untapped creatures you control with flying in addition to paying its other costs.\nDestroy all creatures. Create three 1/1 black Faerie Rogue creature tokens with flying.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/t/tempt_with_glory.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ManaCost:5 W
Types:Sorcery
A:SP$ PutCounterAll | Cost$ 5 W | ValidCards$ Creature.YouCtrl | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBRepeat | StackDescription$ SpellDescription | SpellDescription$ Tempting offer — Put a +1/+1 counter on each creature you control. Each opponent may put a +1/+1 counter on each creature they control. For each opponent who does, put a +1/+1 counter on each creature you control.
SVar:DBRepeat:DB$ RepeatEach | RepeatSubAbility$ DBPutCounter | RepeatPlayers$ Player.Opponent | SubAbility$ PutCounterAgain | RepeatOptionalForEachPlayer$ True | RepeatOptionalMessage$ Do you want to put a +1/+1 counter on each creature you control?
SVar:DBPutCounter:DB$ PutCounterAll | ValidCards$ Creature.RememberedPlayerCtrl | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBCount
SVar:DBPutCounter:DB$ PutCounterAll | ValidCards$ Creature.RememberedPlayerCtrl | Placer$ Controller | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBCount
SVar:DBCount:DB$ StoreSVar | SVar$ X | Type$ CountSVar | Expression$ X/Plus.1
SVar:PutCounterAgain:DB$ PutCounterAll | ValidCards$ Creature.YouCtrl | CounterType$ P1P1 | CounterNum$ X | SubAbility$ DBReset | StackDescription$ None
SVar:DBReset:DB$ StoreSVar | SVar$ X | Type$ Number | Expression$ 0
Expand Down

0 comments on commit d93d9b9

Please sign in to comment.