diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index c5badd51b39..4fc55a2339d 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -657,6 +657,17 @@ public static int getMaxXValue(SpellAbility sa, Player ai, final boolean effect) if (root.costHasManaX()) { val = ComputerUtilMana.determineLeftoverMana(root, ai, effect); + + if (sa.hasParam("AIMaxTgtCost")) { + String value = sa.getParam("AIMaxTgtCost"); + String svar = source.getSVar(value); + if (svar.contains("LeftoverMana")) { + // limit the CMC to available mana + svar = svar.replace("LeftoverMana", ""+val); + } + int calculated = AbilityUtils.calculateAmount(source, svar, sa); + val = ObjectUtils.min(val, calculated); + } } if (sa.usesTargeting()) { diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerSimulationTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerSimulationTest.java index df3a5b2d282..6e1470c95c5 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerSimulationTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerSimulationTest.java @@ -8,6 +8,7 @@ import forge.item.PaperCard; import forge.model.FModel; import org.testng.AssertJUnit; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import forge.game.Game; @@ -897,4 +898,47 @@ public void testPithingNeedlePreventsAbilitiesFromBeingChosen() { game.getAction().checkStateEffects(true); AssertJUnit.assertNull(picker.chooseSpellAbilityToPlay(null)); } + + @DataProvider(name = "testXIsTheLargestPayableCMCData") + public static Object[][] testXIsTheLargestPayableCMCData() { + return new Object[][] { + {"Green Sun's Zenith"}, + //{"Finale of Devastation"}, //X is not limited to allow reach 10 and gain +X/+X + //{"Rocco, Cabaretti Caterer"}, //not working: it is creature with TrigChangeZone + {"Chord of Calling"}, + {"Nature's Rhythm"} + }; + } + + @Test(dataProvider = "testXIsTheLargestPayableCMCData") + public void testXIsTheLargestPayableCMC(String cardName) { + Game game = initAndCreateGame(); + Player ai = game.getPlayers().get(1); + ai.setTeam(0); + + Player opponent = game.getPlayers().get(0); + opponent.setTeam(1); + + // 9 mana available + addCards("Forest", 3, ai); + addCards("Mountain", 3, ai); + addCards("Plains", 3, ai); + + // the most expensive payable creature is Endurance with CMC=3 + addCardToZone(cardName, ai, ZoneType.Hand); + addCardToZone("Birds of Paradise", ai, ZoneType.Library); //CMC 1 - subpar + addCardToZone("Grizzly Bears", ai, ZoneType.Library); //CMC 2 - subpar + addCardToZone("Endurance", ai, ZoneType.Library); //CMC 3 - maximum + addCardToZone("Apex Devastator", ai, ZoneType.Library); //CMC 10 - to much + addCardToZone("Emrakul, the Aeons Torn", ai, ZoneType.Library); //CMC 15 - to much + + game.getPhaseHandler().devModeSet(PhaseType.MAIN2, ai); + game.getAction().checkStateEffects(true); + SpellAbilityPicker picker = new SpellAbilityPicker(game, ai); + SpellAbility sa = picker.chooseSpellAbilityToPlay(null); + + AssertJUnit.assertNotNull(sa); + AssertJUnit.assertEquals("Card '%s' was cast with X instead".formatted(cardName), 3, sa.getXManaCostPaid().intValue()); + } + } diff --git a/forge-gui/res/cardsfolder/c/chord_of_calling.txt b/forge-gui/res/cardsfolder/c/chord_of_calling.txt index dabe0696e14..bca5a9ef867 100644 --- a/forge-gui/res/cardsfolder/c/chord_of_calling.txt +++ b/forge-gui/res/cardsfolder/c/chord_of_calling.txt @@ -2,6 +2,7 @@ Name:Chord of Calling ManaCost:X G G G Types:Instant K:Convoke -A:SP$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.cmcLEX | ChangeNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Search your library for a creature card with mana value X or less, put it onto the battlefield, then shuffle. +A:SP$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.cmcLEX | ChangeNum$ 1 | StackDescription$ SpellDescription | AIMaxTgtCost$ Y | SpellDescription$ Search your library for a creature card with mana value X or less, put it onto the battlefield, then shuffle. SVar:X:Count$xPaid +SVar:Y:Count$ValidLibrary Creature.YouOwn+cmcLELeftoverMana$GreatestCardManaCost Oracle:Convoke (Your creatures can help cast this spell. Each creature you tap while casting this spell pays for {1} or one mana of that creature's color.)\nSearch your library for a creature card with mana value X or less, put it onto the battlefield, then shuffle. diff --git a/forge-gui/res/cardsfolder/f/finale_of_devastation.txt b/forge-gui/res/cardsfolder/f/finale_of_devastation.txt index 63ba9d25c15..0e1c374250a 100644 --- a/forge-gui/res/cardsfolder/f/finale_of_devastation.txt +++ b/forge-gui/res/cardsfolder/f/finale_of_devastation.txt @@ -1,7 +1,7 @@ Name:Finale of Devastation ManaCost:X G G Types:Sorcery -A:SP$ ChangeZone | ChangeType$ Creature.YouCtrl+cmcLEX | Hidden$ True | Origin$ Library | OriginAlternative$ Graveyard | Destination$ Battlefield | ShuffleNonMandatory$ True | SubAbility$ DBPump | SpellDescription$ Search your library and/or graveyard for a creature card with mana value X or less and put it onto the battlefield. If you search your library this way, shuffle. +A:SP$ ChangeZone | ChangeType$ Creature.YouCtrl+cmcLEX | ChangeTypeDesc$ creature | Hidden$ True | Origin$ Library | OriginAlternative$ Graveyard | Destination$ Battlefield | ShuffleNonMandatory$ True | SubAbility$ DBPump | SpellDescription$ Search your library and/or graveyard for a creature card with mana value X or less and put it onto the battlefield. If you search your library this way, shuffle. SVar:DBPump:DB$ PumpAll | ValidCards$ Creature.YouCtrl | KW$ Haste | NumAtt$ +X | NumDef$ +X | ConditionCheckSVar$ X | ConditionSVarCompare$ GE10 | StackDescription$ SpellDescription | SpellDescription$ If X is 10 or more, creatures you control get +X/+X and gain haste until end of turn. SVar:X:Count$xPaid Oracle:Search your library and/or graveyard for a creature card with mana value X or less and put it onto the battlefield. If you search your library this way, shuffle. If X is 10 or more, creatures you control get +X/+X and gain haste until end of turn. diff --git a/forge-gui/res/cardsfolder/g/green_suns_zenith.txt b/forge-gui/res/cardsfolder/g/green_suns_zenith.txt index 1758679d888..146cc73cd35 100644 --- a/forge-gui/res/cardsfolder/g/green_suns_zenith.txt +++ b/forge-gui/res/cardsfolder/g/green_suns_zenith.txt @@ -1,7 +1,8 @@ Name:Green Sun's Zenith ManaCost:X G Types:Sorcery -A:SP$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.Green+cmcLEX | ChangeNum$ 1 | SubAbility$ DBShuffle | StackDescription$ Search your library for a green creature card with mana value X or less, put it onto the battlefield, then shuffle. | SpellDescription$ Search your library for a green creature card with mana value X or less, put it onto the battlefield, then shuffle. Shuffle CARDNAME into its owner's library. +A:SP$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.Green+cmcLEX | ChangeNum$ 1 | SubAbility$ DBShuffle | AIMaxTgtCost$ Y | StackDescription$ Search your library for a green creature card with mana value X or less, put it onto the battlefield, then shuffle. | SpellDescription$ Search your library for a green creature card with mana value X or less, put it onto the battlefield, then shuffle. Shuffle CARDNAME into its owner's library. SVar:X:Count$xPaid +SVar:Y:Count$ValidLibrary Creature.YouOwn+Green+cmcLELeftoverMana$GreatestCardManaCost SVar:DBShuffle:DB$ ChangeZone | Origin$ Stack | Destination$ Library | Shuffle$ True | Defined$ Parent Oracle:Search your library for a green creature card with mana value X or less, put it onto the battlefield, then shuffle. Shuffle Green Sun's Zenith into its owner's library. diff --git a/forge-gui/res/cardsfolder/n/natures_rhythm.txt b/forge-gui/res/cardsfolder/n/natures_rhythm.txt index 19fd55e32e4..ed5fffdda3f 100644 --- a/forge-gui/res/cardsfolder/n/natures_rhythm.txt +++ b/forge-gui/res/cardsfolder/n/natures_rhythm.txt @@ -1,7 +1,8 @@ Name:Nature's Rhythm ManaCost:X G G Types:Sorcery -A:SP$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.cmcLEX | ChangeNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Search your library for a creature card with mana value X or less, put it onto the battlefield, then shuffle. +A:SP$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.cmcLEX | ChangeNum$ 1 | AIMaxTgtCost$ Y | StackDescription$ SpellDescription | SpellDescription$ Search your library for a creature card with mana value X or less, put it onto the battlefield, then shuffle. SVar:X:Count$xPaid +SVar:Y:Count$ValidLibrary Creature.YouOwn+Green+cmcLELeftoverMana$GreatestCardManaCost K:Harmonize:X G G G G Oracle:Search your library for a creature card with mana value X or less, put it onto the battlefield, then shuffle.\nHarmonize {X}{G}{G}{G}{G} (You may cast this card from your graveyard for its harmonize cost. You may tap a creature you control to reduce that cost by an amount of generic mana equal to its power. Then exile this spell.)