Reworked cost adjuster logic for better support of X and cost modification effects:

Improves:
* refactor: split CostAdjuster logic in multiple parts - prepare X, prepare cost, increase cost, reduce cost;
* refactor: improved VariableManaCost to support min/max values, playable and AI calculations, test framework;
* refactor: improved EarlyTargetCost to support mana costs too (related to #13023);
* refactor: migrated some cards with CostAdjuster and X to EarlyTargetCost (Knollspine Invocation, etc - related to #13023);
* refactor: added shared code for "As an additional cost to cast this spell, discard X creature cards";
* refactor: added shared code for "X is the converted mana cost of the exiled card";
* tests: added dozens tests with cost adjusters;

Bug fixes:
* game: fixed that some cards with CostAdjuster ignore min/max limits for X (allow to choose any X, example: Scorched Earth, Open The Way);
* game: fixed that some cards ask to announce already defined X values (example: Bargaining Table);
* game: fixed that some cards with CostAdjuster do not support combo with other cost modification effects;
* game, gui: fixed missing game logs about predefined X values;
* game, gui: fixed wrong X icon for predefined X values;

Test framework:
* test framework: added X min/max check for wrong values;
* test framework: added X min/max info in miss X value announce;
* test framework: added check to find duplicated effect bugs (see assertNoDuplicatedEffects);

Cards:
* Open The Way - fixed that it allow to choose any X without limits (close #12810);
* Unbound Flourishing - improved combo support for activated abilities with predefined X mana costs like Bargaining Table;
This commit is contained in:
Oleg Agafonov 2025-04-08 22:39:10 +04:00
parent 13a832ae00
commit bae3089abb
100 changed files with 1519 additions and 449 deletions

View file

@ -126,7 +126,7 @@ public interface Ability extends Controllable, Serializable {
/**
* Gets all {@link ManaCosts} associated with this ability. These returned
* costs should never be modified as they represent the base costs before
* any modifications.
* any modifications (only cost adjusters can change it, e.g. set min/max values)
*
* @return All {@link ManaCosts} that must be paid.
*/
@ -151,6 +151,17 @@ public interface Ability extends Controllable, Serializable {
void addManaCostsToPay(ManaCost manaCost);
/**
* Helper method to setup actual min/max limits of current X costs BEFORE player's X announcement
*/
void setVariableCostsMinMax(int min, int max);
/**
* Helper method to replace X by direct value BEFORE player's X announcement
* If you need additional target for X then use CostAdjuster + EarlyTargetCost (example: Bargaining Table)
*/
void setVariableCostsValue(int xValue);
/**
* Gets a map of the cost tags (set while casting/activating) of this ability, can be null if no tags have been set yet.
* Does NOT return the source permanent's tags.
@ -356,7 +367,7 @@ public interface Ability extends Controllable, Serializable {
* - for dies triggers - override and use TriggeredAbilityImpl.isInUseableZoneDiesTrigger inside + set setLeavesTheBattlefieldTrigger(true)
*
* @param sourceObject can be null for static continues effects checking like rules modification (example: Yixlid Jailer)
* @param event can be null for state base effects checking like "when you control seven or more" (example: Endrek Sahr, Master Breeder)
* @param event can be null for state base effects checking like "when you control seven or more" (example: Endrek Sahr, Master Breeder)
*/
boolean isInUseableZone(Game game, MageObject sourceObject, GameEvent event);
@ -533,11 +544,25 @@ public interface Ability extends Controllable, Serializable {
void adjustTargets(Game game);
/**
* Dynamic X and cost modification, see CostAdjuster for more details on usage
*/
Ability setCostAdjuster(CostAdjuster costAdjuster);
CostAdjuster getCostAdjuster();
/**
* Prepare {X} settings for announce
*/
void adjustX(Game game);
void adjustCosts(Game game);
/**
* Prepare costs (generate due game state or announce)
*/
void adjustCostsPrepare(Game game);
/**
* Apply additional cost modifications logic/effects
*/
void adjustCostsModify(Game game, CostModificationType costModificationType);
List<Hint> getHints();