mirror of
https://github.com/correl/mage.git
synced 2025-03-07 20:53:18 -10:00
[MH1] added Unbound Flourishing
This commit is contained in:
parent
3599d6343c
commit
12fc854777
16 changed files with 457 additions and 51 deletions
|
@ -2,6 +2,7 @@ package mage.player.ai;
|
|||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.AbilityImpl;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.common.PassAbility;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
|
@ -142,8 +143,12 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
}
|
||||
}
|
||||
// add the specific value for x
|
||||
int xMultiplier = 1;
|
||||
if (newAbility instanceof AbilityImpl) {
|
||||
xMultiplier = ((AbilityImpl) newAbility).handleManaXMultiplier(game, xMultiplier);
|
||||
}
|
||||
newAbility.getManaCostsToPay().add(new ManaCostsImpl(new StringBuilder("{").append(xAmount).append('}').toString()));
|
||||
newAbility.getManaCostsToPay().setX(xAmount);
|
||||
newAbility.getManaCostsToPay().setX(xAmount, xMultiplier);
|
||||
if (varCost != null) {
|
||||
varCost.setPaid();
|
||||
}
|
||||
|
|
|
@ -1502,18 +1502,20 @@ public class ComputerPlayer 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) {
|
||||
log.debug("announceXMana");
|
||||
//TODO: improve this
|
||||
int xMin = min * multilier;
|
||||
int xMax = (max == Integer.MAX_VALUE ? max : max * multilier);
|
||||
int numAvailable = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost();
|
||||
if (numAvailable < 0) {
|
||||
numAvailable = 0;
|
||||
} else {
|
||||
if (numAvailable < min) {
|
||||
numAvailable = min;
|
||||
if (numAvailable < xMin) {
|
||||
numAvailable = xMin;
|
||||
}
|
||||
if (numAvailable > max) {
|
||||
numAvailable = max;
|
||||
if (numAvailable > xMax) {
|
||||
numAvailable = xMax;
|
||||
}
|
||||
}
|
||||
return numAvailable;
|
||||
|
|
|
@ -1128,21 +1128,17 @@ public class HumanPlayer extends PlayerImpl {
|
|||
/**
|
||||
* Gets the amount of mana the player want to spent for a x spell
|
||||
*
|
||||
* @param min
|
||||
* @param max
|
||||
* @param message
|
||||
* @param game
|
||||
* @param ability
|
||||
* @return
|
||||
* @param multilier - X multiplier after replace events
|
||||
*/
|
||||
@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) {
|
||||
int xValue = 0;
|
||||
String extraMessage = (multilier == 1 ? "" : ", X will be increased by " + multilier + " times");
|
||||
updateGameStatePriority("announceXMana", game);
|
||||
do {
|
||||
prepareForResponse(game);
|
||||
if (!isExecutingMacro()) {
|
||||
game.fireGetAmountEvent(playerId, message, min, max);
|
||||
game.fireGetAmountEvent(playerId, message + extraMessage, min, max);
|
||||
}
|
||||
waitForResponse(game);
|
||||
} while (response.getInteger() == null
|
||||
|
|
189
Mage.Sets/src/mage/cards/u/UnboundFlourishing.java
Normal file
189
Mage.Sets/src/mage/cards/u/UnboundFlourishing.java
Normal file
|
@ -0,0 +1,189 @@
|
|||
package mage.cards.u;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackAbility;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public final class UnboundFlourishing extends CardImpl {
|
||||
|
||||
final static String needPrefix = "_UnboundFlourishing_NeedCopy";
|
||||
|
||||
public UnboundFlourishing(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
|
||||
|
||||
// Whenever you cast a permanent spell with a mana cost that contains {X}, double the value of X.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UnboundFlourishingDoubleXEffect()));
|
||||
|
||||
// Whenever you cast an instant or sorcery spell or activate an ability,
|
||||
// if that spell’s mana cost or that ability’s activation cost contains {X}, copy that spell or ability.
|
||||
// You may choose new targets for the copy.
|
||||
this.addAbility(new UnboundFlourishingCopyAbility());
|
||||
}
|
||||
|
||||
public UnboundFlourishing(final UnboundFlourishing card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnboundFlourishing copy() {
|
||||
return new UnboundFlourishing(this);
|
||||
}
|
||||
}
|
||||
|
||||
class UnboundFlourishingDoubleXEffect extends ReplacementEffectImpl {
|
||||
|
||||
UnboundFlourishingDoubleXEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit, false);
|
||||
staticText = "Whenever you cast a permanent spell with a mana cost that contains {X}, double the value of X";
|
||||
}
|
||||
|
||||
UnboundFlourishingDoubleXEffect(final UnboundFlourishingDoubleXEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
event.setAmount(event.getAmount() * 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.X_MANA_ANNOUNCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
Spell spell = game.getSpell(event.getTargetId());
|
||||
return spell != null && spell.isPermanent() && spell.isControlledBy(source.getControllerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnboundFlourishingDoubleXEffect copy() {
|
||||
return new UnboundFlourishingDoubleXEffect(this);
|
||||
}
|
||||
}
|
||||
|
||||
class UnboundFlourishingCopyAbility extends TriggeredAbilityImpl {
|
||||
|
||||
UnboundFlourishingCopyAbility() {
|
||||
super(Zone.BATTLEFIELD, new UnboundFlourishingCopyEffect(), false);
|
||||
}
|
||||
|
||||
UnboundFlourishingCopyAbility(final UnboundFlourishingCopyAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnboundFlourishingCopyAbility copy() {
|
||||
return new UnboundFlourishingCopyAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY
|
||||
|| event.getType() == GameEvent.EventType.SPELL_CAST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getPlayerId().equals(getControllerId())) {
|
||||
|
||||
// activated ability
|
||||
if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) {
|
||||
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
|
||||
if (stackAbility != null && !(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) {
|
||||
if (stackAbility.getManaCostsToPay().containsX()) {
|
||||
game.getState().setValue(this.getSourceId() + UnboundFlourishing.needPrefix, stackAbility);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// spell
|
||||
if (event.getType() == GameEvent.EventType.SPELL_CAST) {
|
||||
Spell spell = game.getStack().getSpell(event.getTargetId());
|
||||
if (spell != null && spell.isInstantOrSorcery()) {
|
||||
game.getState().setValue(this.getSourceId() + UnboundFlourishing.needPrefix, spell);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you cast an instant or sorcery spell or activate an ability, if that spell’s mana cost or that ability’s activation cost contains {X}" + super.getRule();
|
||||
}
|
||||
}
|
||||
|
||||
class UnboundFlourishingCopyEffect extends OneShotEffect {
|
||||
|
||||
UnboundFlourishingCopyEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = ", copy that spell or ability. You may choose new targets for the copy";
|
||||
}
|
||||
|
||||
UnboundFlourishingCopyEffect(final UnboundFlourishingCopyEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnboundFlourishingCopyEffect copy() {
|
||||
return new UnboundFlourishingCopyEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
||||
if (player != null && controller != null) {
|
||||
Object needObject = game.getState().getValue(source.getSourceId() + UnboundFlourishing.needPrefix);
|
||||
|
||||
// copy ability
|
||||
if (needObject instanceof StackAbility) {
|
||||
StackAbility stackAbility = (StackAbility) needObject;
|
||||
stackAbility.createCopyOnStack(game, source, source.getControllerId(), true);
|
||||
game.informPlayers(sourcePermanent.getName() + ": " + controller.getLogName() + " copied activated ability");
|
||||
return true;
|
||||
}
|
||||
|
||||
// copy spell
|
||||
if (needObject instanceof Spell) {
|
||||
Spell spell = (Spell) needObject;
|
||||
spell.createCopyOnStack(game, source, source.getControllerId(), true);
|
||||
game.informPlayers(sourcePermanent.getName() + ": " + controller.getLogName() + " copied casted spell");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -34,7 +34,10 @@ public final class WordOfCommand extends CardImpl {
|
|||
public WordOfCommand(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}{B}");
|
||||
|
||||
// Look at target opponent's hand and choose a card from it. You control that player until Word of Command finishes resolving. The player plays that card if able. While doing so, the player can activate mana abilities only if they're from lands that player controls and only if mana they produce is spent to activate other mana abilities of lands the player controls and/or to play that card. If the chosen card is cast as a spell, you control the player while that spell is resolving.
|
||||
// Look at target opponent's hand and choose a card from it. You control that player until Word of Command finishes resolving.
|
||||
// The player plays that card if able. While doing so, the player can activate mana abilities only if they're from lands that player controls
|
||||
// and only if mana they produce is spent to activate other mana abilities of lands the player controls and/or to play that card.
|
||||
// If the chosen card is cast as a spell, you control the player while that spell is resolving.
|
||||
this.getSpellAbility().addEffect(new WordOfCommandEffect());
|
||||
this.getSpellAbility().addTarget(new TargetOpponent());
|
||||
}
|
||||
|
|
|
@ -253,6 +253,7 @@ public final class ModernHorizons extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Twin-Silk Spider", 188, Rarity.COMMON, mage.cards.t.TwinSilkSpider.class));
|
||||
cards.add(new SetCardInfo("Twisted Reflection", 74, Rarity.UNCOMMON, mage.cards.t.TwistedReflection.class));
|
||||
cards.add(new SetCardInfo("Umezawa's Charm", 111, Rarity.COMMON, mage.cards.u.UmezawasCharm.class));
|
||||
cards.add(new SetCardInfo("Unbound Flourishing", 189, Rarity.MYTHIC, mage.cards.u.UnboundFlourishing.class));
|
||||
cards.add(new SetCardInfo("Undead Augur", 112, Rarity.UNCOMMON, mage.cards.u.UndeadAugur.class));
|
||||
cards.add(new SetCardInfo("Unearth", 113, Rarity.COMMON, mage.cards.u.Unearth.class));
|
||||
cards.add(new SetCardInfo("Universal Automaton", 235, Rarity.COMMON, mage.cards.u.UniversalAutomaton.class));
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
package org.mage.test.cards.continuous;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class UnboundFlourishingTest extends CardTestPlayerBase {
|
||||
|
||||
// Unbound Flourishing
|
||||
|
||||
// Whenever you cast a permanent spell with a mana cost that contains {X}, double the value of X.
|
||||
|
||||
// Whenever you cast an instant or sorcery spell or activate an ability, if that spell’s mana cost or that ability’s activation cost contains {X},
|
||||
// copy that spell or ability. You may choose new targets for the copy.
|
||||
|
||||
@Test
|
||||
public void test_OnCastPermanent_MustDoubleX() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Unbound Flourishing", 1);
|
||||
//
|
||||
// Endless One enters the battlefield with X +1/+1 counters on it.
|
||||
addCard(Zone.HAND, playerA, "Endless One", 1); // {X}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
|
||||
// cast with X=3, but double it
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Endless One");
|
||||
setChoice(playerA, "X=3");
|
||||
checkPermanentCounters("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Endless One", CounterType.P1P1, 3 * 2);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnCastPermanent_MustDoubleX_MultipleTimes() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Unbound Flourishing", 2);
|
||||
//
|
||||
// Endless One enters the battlefield with X +1/+1 counters on it.
|
||||
addCard(Zone.HAND, playerA, "Endless One", 1); // {X}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
|
||||
// cast with X=3, but double it
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Endless One");
|
||||
setChoice(playerA, "X=3");
|
||||
setChoice(playerA, "Unbound Flourishing"); // choose replacement effects
|
||||
checkPermanentCounters("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Endless One", CounterType.P1P1, 3 * 2 * 2);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnCastInstantOrSourcery_MustCopy() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Unbound Flourishing", 1);
|
||||
//
|
||||
// Banefire deals X damage to any target.
|
||||
addCard(Zone.HAND, playerA, "Banefire", 1); // {X}{R}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
|
||||
// cast with X=3 and make copy with another target, not double X
|
||||
checkLife("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 20);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Banefire", playerA);
|
||||
setChoice(playerA, "X=3");
|
||||
setChoice(playerA, "Yes"); // change target
|
||||
addTarget(playerA, playerB); // change to B
|
||||
checkLife("after", 1, PhaseStep.BEGIN_COMBAT, playerA, 20 - 3); // original damage
|
||||
checkLife("after", 1, PhaseStep.BEGIN_COMBAT, playerB, 20 - 3); // copy damage
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnCastPermanent_MustIgnoreAdditionCost() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Unbound Flourishing", 1);
|
||||
//
|
||||
// As an additional cost to cast this spell, pay X life.
|
||||
// Each other player loses X life.
|
||||
addCard(Zone.HAND, playerA, "Bond of Agony", 1); // {X}{B}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
|
||||
|
||||
// cast with X=3, pay addition (normal X) and apply effect (double X)
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bond of Agony");
|
||||
setChoice(playerA, "X=3");
|
||||
checkLife("after", 1, PhaseStep.BEGIN_COMBAT, playerA, 20 - 3); // addition cost X
|
||||
checkLife("after", 1, PhaseStep.BEGIN_COMBAT, playerB, 20 - 3 * 2); // damage double X
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivatedAbility_MustCopy1() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Unbound Flourishing", 1);
|
||||
//
|
||||
// {X}: Put X tower counters on Helix Pinnacle.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Helix Pinnacle", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
|
||||
// pux 3 counters two times
|
||||
checkPermanentCounters("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Helix Pinnacle", CounterType.TOWER, 0);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}:");
|
||||
setChoice(playerA, "X=3");
|
||||
// it haven't target to change
|
||||
checkPermanentCounters("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Helix Pinnacle", CounterType.TOWER, 3 + 3);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivatedAbility_MustCopy1_MultipleTimes() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Unbound Flourishing", 2);
|
||||
//
|
||||
// {X}: Put X tower counters on Helix Pinnacle.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Helix Pinnacle", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
|
||||
// pux 3 counters two times from two cards
|
||||
checkPermanentCounters("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Helix Pinnacle", CounterType.TOWER, 0);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}:");
|
||||
setChoice(playerA, "X=3");
|
||||
setChoice(playerA, "Whenever you cast an instant or sorcery spell"); // choose triggered abilities from two instances
|
||||
// it haven't target to change
|
||||
checkPermanentCounters("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Helix Pinnacle", CounterType.TOWER, 3 + 3 * 2);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivatedAbility_MustCopy2() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Unbound Flourishing", 1);
|
||||
//
|
||||
// {X}{R}, {T}, Sacrifice Cinder Elemental: It deals X damage to any target.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Cinder Elemental", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
|
||||
// activate with X=3 and make copy with another target, not double X
|
||||
checkLife("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 20);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{X}{R}", playerA);
|
||||
setChoice(playerA, "X=3");
|
||||
setChoice(playerA, "Yes"); // change target
|
||||
addTarget(playerA, playerB); // change to B
|
||||
checkLife("after", 1, PhaseStep.BEGIN_COMBAT, playerA, 20 - 3); // original damage
|
||||
checkLife("after", 1, PhaseStep.BEGIN_COMBAT, playerB, 20 - 3); // copy damage
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
}
|
|
@ -1994,7 +1994,7 @@ public class TestPlayer 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) {
|
||||
if (!choices.isEmpty()) {
|
||||
for (String choice : choices) {
|
||||
if (choice.startsWith("X=")) {
|
||||
|
@ -2006,7 +2006,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
this.chooseStrictModeFailed(game, getInfo(ability) + "; " + message);
|
||||
return computerPlayer.announceXMana(min, max, message, game, ability);
|
||||
return computerPlayer.announceXMana(min, max, multilier, message, game, ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -912,7 +912,7 @@ public class PlayerStub 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 min;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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")) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue