implement [MH3] Primal Prayers ; use choice panel for cast mode choice ; allow some restricted "as thought as it had flash" to work only on matching alternative cast. (#12420)

This commit is contained in:
Susucre 2024-07-30 15:47:39 +02:00 committed by GitHub
parent 503e842b51
commit c0eab28626
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
67 changed files with 1105 additions and 596 deletions

View file

@ -19,6 +19,9 @@ import mage.abilities.hint.Hint;
import mage.abilities.icon.CardIcon;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.Card;
import mage.choices.Choice;
import mage.choices.ChoiceHintType;
import mage.choices.ChoiceImpl;
import mage.constants.*;
import mage.game.Game;
import mage.game.command.Dungeon;
@ -255,7 +258,7 @@ public abstract class AbilityImpl implements Ability {
}
@Override
public boolean activate(Game game, boolean noMana) {
public boolean activate(Game game, Set<MageIdentifier> allowedIdentifiers, boolean noMana) {
Player controller = game.getPlayer(this.getControllerId());
if (controller == null) {
return false;
@ -302,7 +305,9 @@ public abstract class AbilityImpl implements Ability {
// or her intentions to pay any or all of those costs (see rule 601.2e).
// A player can't apply two alternative methods of casting or two alternative costs to a single spell.
if (isMainPartAbility) {
activateAlternateOrAdditionalCosts(sourceObject, noMana, controller, game);
if (!activateAlternateOrAdditionalCosts(sourceObject, allowedIdentifiers, noMana, controller, game)) {
return false;
}
}
// 117.6. Some mana costs contain no mana symbols. This represents an unpayable cost. An ability can
@ -451,8 +456,11 @@ public abstract class AbilityImpl implements Ability {
return activated;
}
/**
* @return true if choices for the activation were made (can be to activate with the regular cost)
*/
@Override
public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) {
public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, Set<MageIdentifier> allowedIdentifiers, boolean noMana, Player controller, Game game) {
boolean canUseAlternativeCost = true;
boolean canUseAdditionalCost = true;
@ -494,48 +502,104 @@ public abstract class AbilityImpl implements Ability {
canUseAlternativeCost = false;
}
boolean alternativeCostUsed = false;
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
// it's important to apply alternative cost first
// example: Omniscience gives free mana as alternative, but Entwine ability adds {2} as additional
Abilities<Ability> abilities = CardUtil.getAbilities(sourceObject, game);
// TODO: Why the check for permanent?
if (sourceObject == null || sourceObject instanceof Permanent) {
return true;
}
// 1. ALTERNATIVE COSTS
for (Ability ability : abilities) {
// if cast for noMana no Alternative costs are allowed
if (canUseAlternativeCost && !noMana && ability instanceof AlternativeSourceCosts) {
AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability;
if (alternativeSpellCosts.isAvailable(this, game)) {
if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) {
// only one alternative costs may be activated
alternativeCostUsed = true;
break;
}
}
}
}
// controller specific alternate spell costs
if (canUseAlternativeCost && !noMana && !alternativeCostUsed) {
for (AlternativeSourceCosts alternativeSourceCosts : controller.getAlternativeSourceCosts()) {
if (alternativeSourceCosts.isAvailable(this, game)) {
if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) {
// only one alternative costs may be activated
alternativeCostUsed = true;
break;
}
}
}
}
// it's important to apply alternative cost first
// example: Omniscience gives free mana as alternative, but Entwine ability adds {2} as additional
Abilities<Ability> abilities = CardUtil.getAbilities(sourceObject, game);
// 2. ADDITIONAL COST
for (Ability ability : abilities) {
if (canUseAdditionalCost && ability instanceof OptionalAdditionalSourceCosts) {
((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game);
// 1. ALTERNATIVE COSTS
// Collect all possible alternatives costs:
List<AlternativeSourceCosts> possibleAlternatives = new ArrayList<>();
for (Ability ability : abilities) {
// if cast for noMana no Alternative costs are allowed
if (canUseAlternativeCost && !noMana && ability instanceof AlternativeSourceCosts) {
AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability;
if (alternativeSpellCosts.isAvailable(this, game)
&& alternativeSpellCosts.canActivateAlternativeCostsNow(this, game)
&& (allowedIdentifiers.contains(MageIdentifier.Default) || allowedIdentifiers.contains(ability.getIdentifier()))) {
possibleAlternatives.add(alternativeSpellCosts);
}
}
}
return alternativeCostUsed;
// controller specific alternate spell costs
if (canUseAlternativeCost && !noMana) {
for (AlternativeSourceCosts alternativeSourceCosts : controller.getAlternativeSourceCosts()) {
if (alternativeSourceCosts.isAvailable(this, game)
&& alternativeSourceCosts.canActivateAlternativeCostsNow(this, game)
&& (allowedIdentifiers.contains(MageIdentifier.Default) || allowedIdentifiers.contains(alternativeSourceCosts.getIdentifier()))) {
possibleAlternatives.add(alternativeSourceCosts);
}
}
}
Player player = game.getPlayer(getControllerId());
if (player == null) {
// No controller to activate.
return false;
}
Choice choice = new ChoiceImpl(false); // not required, cancelling will cancel the cast (as you could do once in the pay mana mode).
choice.setSubMessage("for casting " + CardUtil.getSourceLogName(game, "", this, "", ""));
AlternativeSourceCosts alternativeChosen = null;
if (!possibleAlternatives.isEmpty()) {
// At least one alternative cost is available.
// We open a menu for the player to choose up to one.
boolean mustChooseAlternative = !(allowedIdentifiers.contains(MageIdentifier.Default) || allowedIdentifiers.contains(getIdentifier()));
choice.setMessage(
mustChooseAlternative
? "Choose an alternative cost"
: "You may choose an alternative cost"
);
Map<String, Integer> sort = new LinkedHashMap<>();
int i;
for (i = 0; i < possibleAlternatives.size(); i++) {
String key = Integer.toString(i + 1);
sort.put(key, i);
AlternativeSourceCosts alternative = possibleAlternatives.get(i);
MageObject object = alternative.getSourceObject(game);
choice.withItem(
key,
possibleAlternatives.get(i).getAlternativeCostText(this, game),
i,
object != null ? ChoiceHintType.GAME_OBJECT : null,
object != null ? object.getId().toString() : null
);
}
if (!mustChooseAlternative) {
// add the non-alternative cast as the last option.
String key = Integer.toString(i + 1);
sort.put(key, i);
choice.withItem(
key,
"Cast with no alternative cost: " + this.getManaCosts().getText(),
i,
ChoiceHintType.GAME_OBJECT,
sourceObject.getId().toString()
);
}
if (!player.choose(Outcome.Benefit, choice, game)) {
return false;
}
String choiceKey = choice.getChoiceKey();
if (sort.containsKey(choiceKey)) {
int choiceNumber = sort.get(choiceKey);
if (choiceNumber < possibleAlternatives.size()) {
alternativeChosen = possibleAlternatives.get(choiceNumber);
}
}
}
if (alternativeChosen != null) {
alternativeChosen.activateAlternativeCosts(this, game);
}
// 2. ADDITIONAL COST
for (Ability ability : abilities) {
if (canUseAdditionalCost && ability instanceof OptionalAdditionalSourceCosts) {
((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game);
}
}
return true;
}
/**