[MH1] added Unbound Flourishing

This commit is contained in:
Oleg Agafonov 2019-06-06 16:52:06 +04:00
parent 3599d6343c
commit 12fc854777
16 changed files with 457 additions and 51 deletions

View file

@ -254,6 +254,8 @@ public abstract class AbilityImpl implements Ability {
int xValue = this.getManaCostsToPay().getX();
this.getManaCostsToPay().clear();
VariableManaCost xCosts = new VariableManaCost();
// no x events - rules from Unbound Flourishing:
// - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost.
xCosts.setAmount(xValue);
this.getManaCostsToPay().add(xCosts);
} else {
@ -288,11 +290,13 @@ public abstract class AbilityImpl implements Ability {
if (getAbilityType() == AbilityType.SPELL && (getManaCostsToPay().isEmpty() && getCosts().isEmpty()) && !noMana) {
return false;
}
// 20121001 - 601.2b
// If the spell has a variable cost that will be paid as it's being cast (such as an {X} in
// its mana cost; see rule 107.3), the player announces the value of that variable.
VariableManaCost variableManaCost = handleManaXCosts(game, noMana, controller);
String announceString = handleOtherXCosts(game, controller);
// For effects from cards like Void Winnower x costs have to be set
if (this.getAbilityType() == AbilityType.SPELL
&& game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL_LATE, getId(), getSourceId(), getControllerId()), this)) {
@ -499,7 +503,9 @@ public abstract class AbilityImpl implements Ability {
costs.add(fixedCost);
}
// set the xcosts to paid
variableCost.setAmount(xValue);
// no x events - rules from Unbound Flourishing:
// - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost.
variableCost.setAmount(xValue); //
((Cost) variableCost).setPaid();
String message = controller.getLogName() + " announces a value of " + xValue + " (" + variableCost.getActionText() + ')';
announceString.append(message);
@ -530,6 +536,13 @@ public abstract class AbilityImpl implements Ability {
}
}
public int handleManaXMultiplier(Game game, int value) {
// some spells can change X value without new pays (Unbound Flourishing doubles X)
GameEvent xEvent = GameEvent.getEvent(GameEvent.EventType.X_MANA_ANNOUNCE, getId(), getSourceId(), getControllerId(), value);
game.replaceEvent(xEvent, this);
return xEvent.getAmount();
}
/**
* Handles X mana costs and sets manaCostsToPay.
*
@ -546,15 +559,21 @@ public abstract class AbilityImpl implements Ability {
VariableManaCost variableManaCost = null;
for (ManaCost cost : manaCostsToPay) {
if (cost instanceof VariableManaCost) {
variableManaCost = (VariableManaCost) cost;
break; // only one VariableManCost per spell (or is it possible to have more?)
if (variableManaCost == null) {
variableManaCost = (VariableManaCost) cost;
} else {
// only one VariableManCost per spell (or is it possible to have more?)
logger.error("Variable mana cost allowes only in one instance per ability: " + this);
}
}
}
if (variableManaCost != null) {
int xValue;
if (!variableManaCost.isPaid()) { // should only happen for human players
int xValue;
int xValueMultiplier = handleManaXMultiplier(game, 1);
if (!noMana) {
xValue = controller.announceXMana(variableManaCost.getMinX(), variableManaCost.getMaxX(), "Announce the value for " + variableManaCost.getText(), game, this);
xValue = controller.announceXMana(variableManaCost.getMinX(), variableManaCost.getMaxX(), xValueMultiplier,
"Announce the value for " + variableManaCost.getText(), game, this);
int amountMana = xValue * variableManaCost.getMultiplier();
StringBuilder manaString = threadLocalBuilder.get();
if (variableManaCost.getFilter() == null || variableManaCost.getFilter().isGeneric()) {
@ -584,7 +603,7 @@ public abstract class AbilityImpl implements Ability {
}
}
manaCostsToPay.add(new ManaCostsImpl(manaString.toString()));
manaCostsToPay.setX(amountMana);
manaCostsToPay.setX(xValue, xValueMultiplier);
}
variableManaCost.setPaid();
}

View file

@ -1,8 +1,5 @@
package mage.abilities.costs.common;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostImpl;
@ -16,8 +13,11 @@ import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInHand;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public class ExileFromHandCost extends CostImpl {
@ -30,10 +30,9 @@ public class ExileFromHandCost extends CostImpl {
}
/**
*
* @param target
* @param setXFromCMC the spells X value on the stack is set to the
* converted mana costs of the exiled card
* converted mana costs of the exiled card
*/
public ExileFromHandCost(TargetCardInHand target, boolean setXFromCMC) {
this.addTarget(target);
@ -68,6 +67,8 @@ public class ExileFromHandCost extends CostImpl {
paid = true;
if (setXFromCMC) {
VariableManaCost vmc = new VariableManaCost();
// no x events - rules from Unbound Flourishing:
// - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost.
vmc.setAmount(cmc);
vmc.setPaid();
ability.getManaCostsToPay().add(vmc);

View file

@ -1,4 +1,3 @@
package mage.abilities.costs.mana;
import mage.Mana;
@ -11,9 +10,8 @@ import java.util.UUID;
import java.util.stream.Collectors;
/**
*
* @author BetaSteward_at_googlemail.com
* @param <T>
* @author BetaSteward_at_googlemail.com
*/
public interface ManaCosts<T extends ManaCost> extends List<T>, ManaCost {
@ -21,9 +19,15 @@ public interface ManaCosts<T extends ManaCost> extends List<T>, ManaCost {
List<VariableCost> getVariableCosts();
boolean containsX();
int getX();
void setX(int x);
/**
* @param xValue announced X value
* @param xMultiplier special X multiplier to change announced X value without pay increase, see Unbound Flourishing
*/
void setX(int xValue, int xMultiplier);
void load(String mana);
@ -41,7 +45,7 @@ public interface ManaCosts<T extends ManaCost> extends List<T>, ManaCost {
static ManaCosts<ManaCost> removeVariableManaCost(ManaCosts<ManaCost> m) {
return m.stream()
.filter(mc -> !(mc instanceof VariableManaCost))
.collect(Collectors.toCollection(ManaCostsImpl<ManaCost>::new));
.collect(Collectors.toCollection(ManaCostsImpl::new));
}

View file

@ -1,7 +1,5 @@
package mage.abilities.costs.mana;
import java.util.*;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
@ -20,9 +18,11 @@ import mage.players.Player;
import mage.target.Targets;
import mage.util.ManaUtil;
import java.util.*;
/**
* @author BetaSteward_at_googlemail.com
* @param <T>
* @author BetaSteward_at_googlemail.com
*/
public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements ManaCosts<T> {
@ -213,6 +213,11 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
return variableCosts;
}
@Override
public boolean containsX() {
return !getVariableCosts().isEmpty();
}
@Override
public int getX() {
int amount = 0;
@ -224,10 +229,10 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
}
@Override
public void setX(int x) {
public void setX(int xValue, int xMultiplier) {
List<VariableCost> variableCosts = getVariableCosts();
if (!variableCosts.isEmpty()) {
variableCosts.get(0).setAmount(x);
variableCosts.get(0).setAmount(xValue * xMultiplier);
}
}
@ -339,7 +344,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
if (player != null) {
game.undo(playerId);
this.clearPaid();
this.setX(referenceCosts.getX());
this.setX(referenceCosts.getX(), 1); // TODO: checks Word of Command with Unbound Flourishing's X multiplier
player.getManaPool().restoreMana(pool.getPoolBookmark());
game.bookmarkState();
}
@ -378,15 +383,15 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
} else if (!symbol.equals("X")) {
this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0))));
} else // check X wasn't added before
if (modifierForX == 0) {
// count X occurence
for (String s : symbols) {
if (s.equals("X")) {
modifierForX++;
if (modifierForX == 0) {
// count X occurence
for (String s : symbols) {
if (s.equals("X")) {
modifierForX++;
}
}
}
this.add(new VariableManaCost(modifierForX));
} //TODO: handle multiple {X} and/or {Y} symbols
this.add(new VariableManaCost(modifierForX));
} //TODO: handle multiple {X} and/or {Y} symbols
} else if (Character.isDigit(symbol.charAt(0))) {
this.add(new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2))));
} else if (symbol.contains("P")) {

View file

@ -124,6 +124,13 @@ public class GameEvent implements Serializable {
sourceId sourceId of the vehicle
playerId the id of the controlling player
*/
X_MANA_ANNOUNCE,
/* X_MANA_ANNOUNCE
mana x-costs announced by players (X value can be changed by replace events like Unbound Flourishing)
targetId id of the spell that's cast
playerId player that casts the spell
amount X multiplier to change X value, default 1
*/
CAST_SPELL,
/* SPELL_CAST
x-Costs are already defined

View file

@ -564,7 +564,11 @@ public interface Player extends MageItem, Copyable<Player> {
boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
// set the value for X mana spells and abilities
int announceXMana(int min, int max, String message, Game game, Ability ability);
default int announceXMana(int min, int max, String message, Game game, Ability ability) {
return announceXMana(min, max, 1, message, game, ability);
}
int announceXMana(int min, int max, int multilier, String message, Game game, Ability ability);
// set the value for non mana X costs
int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost);

View file

@ -149,7 +149,7 @@ public class StubPlayer extends PlayerImpl implements Player {
}
@Override
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
public int announceXMana(int min, int max, int multilier, String message, Game game, Ability ability) {
return 0;
}