mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-11 16:26:22 +00:00
Compare commits
3 Commits
219e3d6182
...
40e9480c2c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40e9480c2c | ||
|
|
3db276f323 | ||
|
|
c9a5fe9135 |
@@ -1345,9 +1345,7 @@ public class ComputerUtilMana {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!effect) {
|
CostAdjustment.adjust(manaCost, sa, null, test, effect);
|
||||||
CostAdjustment.adjust(manaCost, sa, null, test);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("NumTimes".equals(sa.getParam("Announce"))) { // e.g. the Adversary cycle
|
if ("NumTimes".equals(sa.getParam("Announce"))) { // e.g. the Adversary cycle
|
||||||
ManaCost mkCost = sa.getPayCosts().getTotalMana();
|
ManaCost mkCost = sa.getPayCosts().getTotalMana();
|
||||||
@@ -1773,15 +1771,18 @@ public class ComputerUtilMana {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Matches list of creatures to shards in mana cost for convoking.
|
* Matches list of creatures to shards in mana cost for convoking.
|
||||||
* @param cost cost of convoked ability
|
*
|
||||||
* @param list creatures to be evaluated
|
* @param cost cost of convoked ability
|
||||||
* @param improvise
|
* @param list creatures to be evaluated
|
||||||
|
* @param artifacts
|
||||||
|
* @param creatures
|
||||||
* @return map between creatures and shards to convoke
|
* @return map between creatures and shards to convoke
|
||||||
*/
|
*/
|
||||||
public static Map<Card, ManaCostShard> getConvokeOrImproviseFromList(final ManaCost cost, List<Card> list, boolean improvise) {
|
public static Map<Card, ManaCostShard> getConvokeOrImproviseFromList(final ManaCost cost, List<Card> list, boolean artifacts, boolean creatures) {
|
||||||
final Map<Card, ManaCostShard> convoke = new HashMap<>();
|
final Map<Card, ManaCostShard> convoke = new HashMap<>();
|
||||||
Card convoked = null;
|
Card convoked = null;
|
||||||
if (!improvise) {
|
if (creatures && !artifacts) {
|
||||||
|
// Run for convoke but not improvise or waterbending
|
||||||
for (ManaCostShard toPay : cost) {
|
for (ManaCostShard toPay : cost) {
|
||||||
if (toPay.isSnow() || toPay.isColorless()) {
|
if (toPay.isSnow() || toPay.isColorless()) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -1382,7 +1382,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Card, ManaCostShard> chooseCardsForConvokeOrImprovise(SpellAbility sa, ManaCost manaCost, CardCollectionView untappedCards, boolean improvise) {
|
public Map<Card, ManaCostShard> chooseCardsForConvokeOrImprovise(SpellAbility sa, ManaCost manaCost, CardCollectionView untappedCards, boolean artifacts, boolean creatures, Integer maxReduction) {
|
||||||
final Player ai = sa.getActivatingPlayer();
|
final Player ai = sa.getActivatingPlayer();
|
||||||
final PhaseHandler ph = ai.getGame().getPhaseHandler();
|
final PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||||
//Filter out mana sources that will interfere with payManaCost()
|
//Filter out mana sources that will interfere with payManaCost()
|
||||||
@@ -1390,9 +1390,10 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
// Filter out creatures if AI hasn't attacked yet
|
// Filter out creatures if AI hasn't attacked yet
|
||||||
if (ph.isPlayerTurn(ai) && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
if (ph.isPlayerTurn(ai) && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||||
if (improvise) {
|
if (!creatures) {
|
||||||
untapped = CardLists.filter(untapped, c -> !c.isCreature());
|
untapped = CardLists.filter(untapped, c -> !c.isCreature());
|
||||||
} else {
|
} else {
|
||||||
|
// TODO AI needs to learn how to use Convoke or Waterbend
|
||||||
return new HashMap<>();
|
return new HashMap<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1406,13 +1407,16 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
if (!ai.getGame().getStack().isEmpty()) {
|
if (!ai.getGame().getStack().isEmpty()) {
|
||||||
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), null);
|
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), null);
|
||||||
for (Card c : blockers) {
|
for (Card c : blockers) {
|
||||||
if (objects.contains(c) && (!improvise || c.isArtifact())) {
|
if (objects.contains(c) && (creatures || c.isArtifact())) {
|
||||||
untapped.add(c);
|
untapped.add(c);
|
||||||
}
|
}
|
||||||
|
if (maxReduction != null && untapped.size() >= maxReduction) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ComputerUtilMana.getConvokeOrImproviseFromList(manaCost, untapped, improvise);
|
return ComputerUtilMana.getConvokeOrImproviseFromList(manaCost, untapped, artifacts, creatures);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -75,11 +75,13 @@ public class Game {
|
|||||||
|
|
||||||
private List<Card> activePlanes = null;
|
private List<Card> activePlanes = null;
|
||||||
|
|
||||||
public final Phase cleanup;
|
|
||||||
public final Phase endOfCombat;
|
|
||||||
public final Phase endOfTurn;
|
|
||||||
public final Untap untap;
|
public final Untap untap;
|
||||||
public final Phase upkeep;
|
public final Phase upkeep;
|
||||||
|
public final Phase beginOfCombat;
|
||||||
|
public final Phase endOfCombat;
|
||||||
|
public final Phase endOfTurn;
|
||||||
|
public final Phase cleanup;
|
||||||
|
|
||||||
// to execute commands for "current" phase each time state based action is checked
|
// to execute commands for "current" phase each time state based action is checked
|
||||||
public final List<GameCommand> sbaCheckedCommandList;
|
public final List<GameCommand> sbaCheckedCommandList;
|
||||||
public final MagicStack stack;
|
public final MagicStack stack;
|
||||||
@@ -363,9 +365,10 @@ public class Game {
|
|||||||
|
|
||||||
untap = new Untap(this);
|
untap = new Untap(this);
|
||||||
upkeep = new Phase(PhaseType.UPKEEP);
|
upkeep = new Phase(PhaseType.UPKEEP);
|
||||||
cleanup = new Phase(PhaseType.CLEANUP);
|
beginOfCombat = new Phase(PhaseType.COMBAT_BEGIN);
|
||||||
endOfCombat = new Phase(PhaseType.COMBAT_END);
|
endOfCombat = new Phase(PhaseType.COMBAT_END);
|
||||||
endOfTurn = new Phase(PhaseType.END_OF_TURN);
|
endOfTurn = new Phase(PhaseType.END_OF_TURN);
|
||||||
|
cleanup = new Phase(PhaseType.CLEANUP);
|
||||||
|
|
||||||
sbaCheckedCommandList = new ArrayList<>();
|
sbaCheckedCommandList = new ArrayList<>();
|
||||||
|
|
||||||
@@ -428,6 +431,9 @@ public class Game {
|
|||||||
public final Phase getUpkeep() {
|
public final Phase getUpkeep() {
|
||||||
return upkeep;
|
return upkeep;
|
||||||
}
|
}
|
||||||
|
public final Phase getBeginOfCombat() {
|
||||||
|
return beginOfCombat;
|
||||||
|
}
|
||||||
public final Phase getEndOfCombat() {
|
public final Phase getEndOfCombat() {
|
||||||
return endOfCombat;
|
return endOfCombat;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import forge.game.*;
|
|||||||
import forge.game.ability.AbilityFactory.AbilityRecordType;
|
import forge.game.ability.AbilityFactory.AbilityRecordType;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
|
import forge.game.cost.CostAdjustment;
|
||||||
import forge.game.cost.IndividualCostPaymentInstance;
|
import forge.game.cost.IndividualCostPaymentInstance;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
@@ -1527,6 +1528,7 @@ public class AbilityUtils {
|
|||||||
else {
|
else {
|
||||||
cost = new Cost(unlessCost, true);
|
cost = new Cost(unlessCost, true);
|
||||||
}
|
}
|
||||||
|
cost = CostAdjustment.adjust(cost, sa, true);
|
||||||
return cost;
|
return cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package forge.game.ability.effects;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import forge.GameCommand;
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
@@ -28,10 +27,11 @@ public class ControlPlayerEffect extends SpellAbilityEffect {
|
|||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Player controller = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Controller"), sa).get(0);
|
final Player controller = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Controller"), sa).get(0);
|
||||||
final Game game = controller.getGame();
|
final Game game = controller.getGame();
|
||||||
|
final boolean combat = sa.hasParam("Combat");
|
||||||
|
|
||||||
for (final Player pTarget: getTargetPlayers(sa)) {
|
for (final Player pTarget: getTargetPlayers(sa)) {
|
||||||
// before next untap gain control
|
// before next untap gain control
|
||||||
game.getCleanup().addUntil(pTarget, (GameCommand) () -> {
|
(combat ? game.getBeginOfCombat() : game.getCleanup()).addUntil(pTarget, () -> {
|
||||||
// CR 800.4b
|
// CR 800.4b
|
||||||
if (!controller.isInGame()) {
|
if (!controller.isInGame()) {
|
||||||
return;
|
return;
|
||||||
@@ -41,7 +41,7 @@ public class ControlPlayerEffect extends SpellAbilityEffect {
|
|||||||
pTarget.addController(ts, controller);
|
pTarget.addController(ts, controller);
|
||||||
|
|
||||||
// after following cleanup release control
|
// after following cleanup release control
|
||||||
game.getCleanup().addUntil((GameCommand) () -> pTarget.removeController(ts));
|
(combat ? game.getEndOfCombat() : game.getCleanup()).addUntil(() -> pTarget.removeController(ts));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,6 +188,15 @@ public class Cost implements Serializable {
|
|||||||
return this.isAbility;
|
return this.isAbility;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final String getMaxWaterbend() {
|
||||||
|
for (CostPart cp : this.costParts) {
|
||||||
|
if (cp instanceof CostPartMana) {
|
||||||
|
return ((CostPartMana) cp).getMaxWaterbend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private Cost() {
|
private Cost() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -564,6 +573,11 @@ public class Cost implements Serializable {
|
|||||||
return new CostRevealChosen(splitStr[0], splitStr.length > 1 ? splitStr[1] : null);
|
return new CostRevealChosen(splitStr[0], splitStr.length > 1 ? splitStr[1] : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parse.startsWith("Waterbend<")) {
|
||||||
|
final String[] splitStr = abCostParse(parse, 1);
|
||||||
|
return new CostWaterbend(splitStr[0]);
|
||||||
|
}
|
||||||
|
|
||||||
if (parse.equals("Forage")) {
|
if (parse.equals("Forage")) {
|
||||||
return new CostForage();
|
return new CostForage();
|
||||||
}
|
}
|
||||||
@@ -973,6 +987,7 @@ public class Cost implements Serializable {
|
|||||||
} else {
|
} else {
|
||||||
costParts.add(0, new CostPartMana(manaCost.toManaCost(), null));
|
costParts.add(0, new CostPartMana(manaCost.toManaCost(), null));
|
||||||
}
|
}
|
||||||
|
getCostMana().setMaxWaterbend(mPart.getMaxWaterbend());
|
||||||
} else if (part instanceof CostPutCounter || (mergeAdditional && // below usually not desired because they're from different causes
|
} else if (part instanceof CostPutCounter || (mergeAdditional && // below usually not desired because they're from different causes
|
||||||
(part instanceof CostDiscard || part instanceof CostDraw ||
|
(part instanceof CostDiscard || part instanceof CostDraw ||
|
||||||
part instanceof CostAddMana || part instanceof CostPayLife ||
|
part instanceof CostAddMana || part instanceof CostPayLife ||
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class CostAdjustment {
|
public class CostAdjustment {
|
||||||
|
|
||||||
public static Cost adjust(final Cost cost, final SpellAbility sa, boolean effect) {
|
public static Cost adjust(final Cost cost, final SpellAbility sa, boolean effect) {
|
||||||
if (sa.isTrigger() || cost == null || effect) {
|
if (sa.isTrigger() || cost == null || effect) {
|
||||||
|
sa.setMaxWaterbend(cost);
|
||||||
return cost;
|
return cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +101,9 @@ public class CostAdjustment {
|
|||||||
host.setState(CardStateName.Original, false);
|
host.setState(CardStateName.Original, false);
|
||||||
host.setFaceDown(false);
|
host.setFaceDown(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sa.setMaxWaterbend(result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,21 +176,22 @@ public class CostAdjustment {
|
|||||||
|
|
||||||
// If cardsToDelveOut is null, will immediately exile the delved cards and remember them on the host card.
|
// If cardsToDelveOut is null, will immediately exile the delved cards and remember them on the host card.
|
||||||
// Otherwise, will return them in cardsToDelveOut and the caller is responsible for doing the above.
|
// Otherwise, will return them in cardsToDelveOut and the caller is responsible for doing the above.
|
||||||
public static boolean adjust(ManaCostBeingPaid cost, final SpellAbility sa, CardCollection cardsToDelveOut, boolean test) {
|
public static boolean adjust(ManaCostBeingPaid cost, final SpellAbility sa, CardCollection cardsToDelveOut, boolean test, boolean effect) {
|
||||||
if (sa.isTrigger() || sa.isReplacementAbility()) {
|
if (effect) {
|
||||||
|
adjustCostByWaterbend(cost, sa, test);
|
||||||
|
}
|
||||||
|
if (effect || sa.isTrigger() || sa.isReplacementAbility()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
final Game game = sa.getActivatingPlayer().getGame();
|
||||||
final Card originalCard = sa.getHostCard();
|
final Card originalCard = sa.getHostCard();
|
||||||
boolean isStateChangeToFaceDown = false;
|
|
||||||
|
|
||||||
if (sa.isSpell()) {
|
boolean isStateChangeToFaceDown = false;
|
||||||
if (sa.isCastFaceDown() && !originalCard.isFaceDown()) {
|
if (sa.isSpell() && sa.isCastFaceDown() && !originalCard.isFaceDown()) {
|
||||||
// Turn face down to apply cost modifiers correctly
|
// Turn face down to apply cost modifiers correctly
|
||||||
originalCard.turnFaceDownNoUpdate();
|
originalCard.turnFaceDownNoUpdate();
|
||||||
isStateChangeToFaceDown = true;
|
isStateChangeToFaceDown = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection cardsOnBattlefield = new CardCollection(game.getCardsIn(ZoneType.Battlefield));
|
CardCollection cardsOnBattlefield = new CardCollection(game.getCardsIn(ZoneType.Battlefield));
|
||||||
@@ -278,17 +284,19 @@ public class CostAdjustment {
|
|||||||
table.triggerChangesZoneAll(game, sa);
|
table.triggerChangesZoneAll(game, sa);
|
||||||
}
|
}
|
||||||
if (sa.getHostCard().hasKeyword(Keyword.CONVOKE)) {
|
if (sa.getHostCard().hasKeyword(Keyword.CONVOKE)) {
|
||||||
adjustCostByConvokeOrImprovise(cost, sa, false, test);
|
adjustCostByConvokeOrImprovise(cost, sa, false, true, test);
|
||||||
}
|
}
|
||||||
if (sa.getHostCard().hasKeyword(Keyword.IMPROVISE)) {
|
if (sa.getHostCard().hasKeyword(Keyword.IMPROVISE)) {
|
||||||
adjustCostByConvokeOrImprovise(cost, sa, true, test);
|
adjustCostByConvokeOrImprovise(cost, sa, true, false, test);
|
||||||
}
|
}
|
||||||
} // isSpell
|
} // isSpell
|
||||||
|
|
||||||
if (sa.hasParam("TapCreaturesForMana")) {
|
if (sa.hasParam("TapCreaturesForMana")) {
|
||||||
adjustCostByConvokeOrImprovise(cost, sa, false, test);
|
adjustCostByConvokeOrImprovise(cost, sa, false, true, test);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adjustCostByWaterbend(cost, sa, test);
|
||||||
|
|
||||||
// Reset card state (if changed)
|
// Reset card state (if changed)
|
||||||
if (isStateChangeToFaceDown) {
|
if (isStateChangeToFaceDown) {
|
||||||
originalCard.setFaceDown(false);
|
originalCard.setFaceDown(false);
|
||||||
@@ -299,6 +307,13 @@ public class CostAdjustment {
|
|||||||
}
|
}
|
||||||
// GetSpellCostChange
|
// GetSpellCostChange
|
||||||
|
|
||||||
|
private static void adjustCostByWaterbend(ManaCostBeingPaid cost, SpellAbility sa, boolean test) {
|
||||||
|
Integer maxWaterbend = sa.getMaxWaterbend();
|
||||||
|
if (maxWaterbend != null && maxWaterbend > 0) {
|
||||||
|
adjustCostByConvokeOrImprovise(cost, sa, true, true, test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean adjustCostByAssist(ManaCostBeingPaid cost, final SpellAbility sa, boolean test) {
|
private static boolean adjustCostByAssist(ManaCostBeingPaid cost, final SpellAbility sa, boolean test) {
|
||||||
// 702.132a Assist is a static ability that modifies the rules of paying for the spell with assist (see rules 601.2g-h).
|
// 702.132a Assist is a static ability that modifies the rules of paying for the spell with assist (see rules 601.2g-h).
|
||||||
// If the total cost to cast a spell with assist includes a generic mana component, before you activate mana abilities while casting it, you may choose another player.
|
// If the total cost to cast a spell with assist includes a generic mana component, before you activate mana abilities while casting it, you may choose another player.
|
||||||
@@ -321,27 +336,33 @@ public class CostAdjustment {
|
|||||||
return assistant.getController().helpPayForAssistSpell(cost, sa, genericLeft, requestedAmount);
|
return assistant.getController().helpPayForAssistSpell(cost, sa, genericLeft, requestedAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void adjustCostByConvokeOrImprovise(ManaCostBeingPaid cost, final SpellAbility sa, boolean improvise, boolean test) {
|
private static void adjustCostByConvokeOrImprovise(ManaCostBeingPaid cost, final SpellAbility sa, boolean artifacts, boolean creatures, boolean test) {
|
||||||
if (!improvise) {
|
if (creatures && !artifacts) {
|
||||||
sa.clearTappedForConvoke();
|
sa.clearTappedForConvoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Player activator = sa.getActivatingPlayer();
|
final Player activator = sa.getActivatingPlayer();
|
||||||
CardCollectionView untappedCards = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield),
|
CardCollectionView untappedCards = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield),
|
||||||
CardPredicates.CAN_TAP);
|
CardPredicates.CAN_TAP);
|
||||||
if (improvise) {
|
|
||||||
|
Integer maxReduction = null;
|
||||||
|
if (artifacts && creatures) {
|
||||||
|
maxReduction = sa.getMaxWaterbend();
|
||||||
|
Predicate <Card> isArtifactOrCreature = card -> card.isArtifact() || card.isCreature();
|
||||||
|
untappedCards = CardLists.filter(untappedCards, isArtifactOrCreature);
|
||||||
|
} else if (artifacts) {
|
||||||
untappedCards = CardLists.filter(untappedCards, CardPredicates.ARTIFACTS);
|
untappedCards = CardLists.filter(untappedCards, CardPredicates.ARTIFACTS);
|
||||||
} else {
|
} else {
|
||||||
untappedCards = CardLists.filter(untappedCards, CardPredicates.CREATURES);
|
untappedCards = CardLists.filter(untappedCards, CardPredicates.CREATURES);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Card, ManaCostShard> convokedCards = activator.getController().chooseCardsForConvokeOrImprovise(sa,
|
Map<Card, ManaCostShard> convokedCards = activator.getController().chooseCardsForConvokeOrImprovise(sa,
|
||||||
cost.toManaCost(), untappedCards, improvise);
|
cost.toManaCost(), untappedCards, artifacts, creatures, maxReduction);
|
||||||
|
|
||||||
CardCollection tapped = new CardCollection();
|
CardCollection tapped = new CardCollection();
|
||||||
for (final Entry<Card, ManaCostShard> conv : convokedCards.entrySet()) {
|
for (final Entry<Card, ManaCostShard> conv : convokedCards.entrySet()) {
|
||||||
Card c = conv.getKey();
|
Card c = conv.getKey();
|
||||||
if (!improvise) {
|
if (creatures && !artifacts) {
|
||||||
sa.addTappedForConvoke(c);
|
sa.addTappedForConvoke(c);
|
||||||
}
|
}
|
||||||
cost.decreaseShard(conv.getValue(), 1);
|
cost.decreaseShard(conv.getValue(), 1);
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ public class CostPartMana extends CostPart {
|
|||||||
private boolean isEnchantedCreatureCost = false;
|
private boolean isEnchantedCreatureCost = false;
|
||||||
private boolean isCostPayAnyNumberOfTimes = false;
|
private boolean isCostPayAnyNumberOfTimes = false;
|
||||||
|
|
||||||
|
protected String maxWaterbend;
|
||||||
|
|
||||||
public int paymentOrder() { return shouldPayLast() ? 200 : 0; }
|
public int paymentOrder() { return shouldPayLast() ? 200 : 0; }
|
||||||
|
|
||||||
public boolean shouldPayLast() {
|
public boolean shouldPayLast() {
|
||||||
@@ -63,6 +65,13 @@ public class CostPartMana extends CostPart {
|
|||||||
this.isEnchantedCreatureCost = enchantedCreatureCost;
|
this.isEnchantedCreatureCost = enchantedCreatureCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMaxWaterbend() {
|
||||||
|
return maxWaterbend;
|
||||||
|
}
|
||||||
|
public void setMaxWaterbend(String max) {
|
||||||
|
maxWaterbend = max;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the mana.
|
* Gets the mana.
|
||||||
*
|
*
|
||||||
@@ -101,7 +110,7 @@ public class CostPartMana extends CostPart {
|
|||||||
public boolean isUndoable() { return true; }
|
public boolean isUndoable() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String toString() {
|
public String toString() {
|
||||||
return cost.toString();
|
return cost.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
forge-game/src/main/java/forge/game/cost/CostWaterbend.java
Normal file
18
forge-game/src/main/java/forge/game/cost/CostWaterbend.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package forge.game.cost;
|
||||||
|
|
||||||
|
import forge.card.mana.ManaCost;
|
||||||
|
import forge.card.mana.ManaCostParser;
|
||||||
|
|
||||||
|
public class CostWaterbend extends CostPartMana {
|
||||||
|
|
||||||
|
public CostWaterbend(final String mana) {
|
||||||
|
super(new ManaCost(new ManaCostParser(mana)), null);
|
||||||
|
|
||||||
|
maxWaterbend = mana;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return "Waterbend " + getMana().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -297,6 +297,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
case COMBAT_BEGIN:
|
case COMBAT_BEGIN:
|
||||||
nCombatsThisTurn++;
|
nCombatsThisTurn++;
|
||||||
combat = new Combat(playerTurn);
|
combat = new Combat(playerTurn);
|
||||||
|
game.getBeginOfCombat().executeUntil(playerTurn);
|
||||||
//PhaseUtil.verifyCombat();
|
//PhaseUtil.verifyCombat();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -756,7 +757,6 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fire blockers declared trigger
|
|
||||||
final Map<AbilityKey, Object> bdRunParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> bdRunParams = AbilityKey.newMap();
|
||||||
bdRunParams.put(AbilityKey.Blockers, declaredBlockers);
|
bdRunParams.put(AbilityKey.Blockers, declaredBlockers);
|
||||||
bdRunParams.put(AbilityKey.Attackers, blockedAttackers);
|
bdRunParams.put(AbilityKey.Attackers, blockedAttackers);
|
||||||
@@ -768,7 +768,6 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run triggers
|
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
runParams.put(AbilityKey.Blocker, c1);
|
runParams.put(AbilityKey.Blocker, c1);
|
||||||
runParams.put(AbilityKey.Attackers, combat.getAttackersBlockedBy(c1));
|
runParams.put(AbilityKey.Attackers, combat.getAttackersBlockedBy(c1));
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ public abstract class PlayerController {
|
|||||||
public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard);
|
public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard);
|
||||||
|
|
||||||
public abstract CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave);
|
public abstract CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave);
|
||||||
public abstract Map<Card, ManaCostShard> chooseCardsForConvokeOrImprovise(SpellAbility sa, ManaCost manaCost, CardCollectionView untappedCards, boolean improvise);
|
public abstract Map<Card, ManaCostShard> chooseCardsForConvokeOrImprovise(SpellAbility sa, ManaCost manaCost, CardCollectionView untappedCards, boolean artifacts, boolean creatures, Integer maxReduction);
|
||||||
public abstract List<Card> chooseCardsForSplice(SpellAbility sa, List<Card> cards);
|
public abstract List<Card> chooseCardsForSplice(SpellAbility sa, List<Card> cards);
|
||||||
|
|
||||||
public abstract CardCollectionView chooseCardsToRevealFromHand(int min, int max, CardCollectionView valid);
|
public abstract CardCollectionView chooseCardsToRevealFromHand(int min, int max, CardCollectionView valid);
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
private final Supplier<CardCollection> tappedForConvoke = Suppliers.memoize(CardCollection::new);
|
private final Supplier<CardCollection> tappedForConvoke = Suppliers.memoize(CardCollection::new);
|
||||||
private Card sacrificedAsOffering;
|
private Card sacrificedAsOffering;
|
||||||
private Card sacrificedAsEmerge;
|
private Card sacrificedAsEmerge;
|
||||||
|
private Integer maxWaterbend;
|
||||||
|
|
||||||
private AbilityManaPart manaPart;
|
private AbilityManaPart manaPart;
|
||||||
|
|
||||||
@@ -2692,4 +2693,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getMaxWaterbend() {
|
||||||
|
return maxWaterbend;
|
||||||
|
}
|
||||||
|
public void setMaxWaterbend(Cost cost) {
|
||||||
|
if (cost == null || cost.getMaxWaterbend() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
maxWaterbend = AbilityUtils.calculateAmount(getHostCard(), cost.getMaxWaterbend(), this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -428,6 +428,10 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
game.getTriggerHandler().runTrigger(TriggerType.AbilityCast, runParams, true);
|
game.getTriggerHandler().runTrigger(TriggerType.AbilityCast, runParams, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sp.getMaxWaterbend() != null) {
|
||||||
|
activator.triggerElementalBend(TriggerType.Waterbend);
|
||||||
|
}
|
||||||
|
|
||||||
// Run Cycled triggers
|
// Run Cycled triggers
|
||||||
if (sp.isCycling()) {
|
if (sp.isCycling()) {
|
||||||
activator.addCycled(sp);
|
activator.addCycled(sp);
|
||||||
|
|||||||
@@ -690,7 +690,7 @@ public class PlayerControllerForTests extends PlayerController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Card, ManaCostShard> chooseCardsForConvokeOrImprovise(SpellAbility sa, ManaCost manaCost,
|
public Map<Card, ManaCostShard> chooseCardsForConvokeOrImprovise(SpellAbility sa, ManaCost manaCost,
|
||||||
CardCollectionView untappedCards, boolean improvise) {
|
CardCollectionView untappedCards, boolean artifacts, boolean creatures, Integer maxReduction) {
|
||||||
// TODO: AI to choose a creature to tap would go here
|
// TODO: AI to choose a creature to tap would go here
|
||||||
// Probably along with deciding how many creatures to tap
|
// Probably along with deciding how many creatures to tap
|
||||||
return new HashMap<>();
|
return new HashMap<>();
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ ManaCost:1 G
|
|||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | SubAbility$ DBDamage | AILogic$ PowerDmg | StackDescription$ {c:ThisTargetedCard} | SpellDescription$ Target creature you control
|
A:SP$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | SubAbility$ DBDamage | AILogic$ PowerDmg | StackDescription$ {c:ThisTargetedCard} | SpellDescription$ Target creature you control
|
||||||
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature | TgtPrompt$ Select another target creature | TargetUnique$ True | AILogic$ PowerDmg | NumDmg$ X | DamageSource$ ParentTarget | ExcessSVar$ Excess | SubAbility$ DBDiscover | StackDescription$ REP another target creature_{c:ThisTargetedCard} | SpellDescription$ deals damage equal to its power to another target creature.
|
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature | TgtPrompt$ Select another target creature | TargetUnique$ True | AILogic$ PowerDmg | NumDmg$ X | DamageSource$ ParentTarget | ExcessSVar$ Excess | SubAbility$ DBDiscover | StackDescription$ REP another target creature_{c:ThisTargetedCard} | SpellDescription$ deals damage equal to its power to another target creature.
|
||||||
SVar:DBDiscover:DB$ Discover | Num$ Excess | StackDescription$ SpellDescription | SpellDescription$ If excess damage was dealt this way, discover X, where X is that excess damage. (Exile cards from the top of your library until you exile a nonland card with that mana value or less. Cast it without paying its mana cost or put it into your hand. Put the rest on the bottom in a random order.)
|
SVar:DBDiscover:DB$ Discover | Num$ Excess | ConditionCheckSVar$ Excess | StackDescription$ SpellDescription | SpellDescription$ If excess damage was dealt this way, discover X, where X is that excess damage. (Exile cards from the top of your library until you exile a nonland card with that mana value or less. Cast it without paying its mana cost or put it into your hand. Put the rest on the bottom in a random order.)
|
||||||
SVar:X:ParentTargeted$CardPower
|
SVar:X:ParentTargeted$CardPower
|
||||||
Oracle:Target creature you control deals damage equal to its power to another target creature. If excess damage was dealt this way, discover X, where X is that excess damage. (Exile cards from the top of your library until you exile a nonland card with that mana value or less. Cast it without paying its mana cost or put it into your hand. Put the rest on the bottom in a random order.)
|
Oracle:Target creature you control deals damage equal to its power to another target creature. If excess damage was dealt this way, discover X, where X is that excess damage. (Exile cards from the top of your library until you exile a nonland card with that mana value or less. Cast it without paying its mana cost or put it into your hand. Put the rest on the bottom in a random order.)
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ Name:Of One Mind
|
|||||||
ManaCost:2 U
|
ManaCost:2 U
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 2 | EffectZone$ All | IsPresent$ Creature.Human+YouCtrl | CheckSVar$ Count$Valid Creature.nonHuman+YouCtrl | Description$ CARDNAME costs {2} less to cast if you control a Human creature and a non-Human creature.
|
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 2 | EffectZone$ All | IsPresent$ Creature.Human+YouCtrl | CheckSVar$ Count$Valid Creature.nonHuman+YouCtrl | Description$ CARDNAME costs {2} less to cast if you control a Human creature and a non-Human creature.
|
||||||
DeckHints:Type$Human
|
|
||||||
A:SP$ Draw | NumCards$ 2 | SpellDescription$ Draw two cards.
|
A:SP$ Draw | NumCards$ 2 | SpellDescription$ Draw two cards.
|
||||||
|
DeckHints:Type$Human
|
||||||
Oracle:This spell costs {2} less to cast if you control a Human creature and a non-Human creature.\nDraw two cards.
|
Oracle:This spell costs {2} less to cast if you control a Human creature and a non-Human creature.\nDraw two cards.
|
||||||
|
|||||||
8
forge-gui/res/cardsfolder/upcoming/geyser_leaper.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/geyser_leaper.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Name:Geyser Leaper
|
||||||
|
ManaCost:4 U
|
||||||
|
Types:Creature Human Warrior Ally
|
||||||
|
PT:4/3
|
||||||
|
K:Flying
|
||||||
|
A:AB$ Draw | Cost$ Waterbend<4> | NumCards$ 1 | SubAbility$ DBDiscard | SpellDescription$ Draw a card, then discard a card. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)
|
||||||
|
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
|
||||||
|
Oracle:Flying\nWaterbend 4: Draw a card, then discard a card.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Name:Ruinous Waterbending
|
||||||
|
ManaCost:1 B B
|
||||||
|
Types:Sorcery Lesson
|
||||||
|
S:Mode$ OptionalCost | EffectZone$ All | ValidCard$ Card.Self | ValidSA$ Spell | Cost$ Waterbend<4> | Description$ As an additional cost to cast this spell, you may waterbend {4}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)
|
||||||
|
A:SP$ PumpAll | ValidCards$ Creature | NumAtt$ -2 | NumDef$ -2 | IsCurse$ True | SubAbility$ DBEffect | SpellDescription$ All creatures get -2/-2 until end of turn. If this spell's additional cost was paid, whenever a creature dies this turn, you gain 1 life.
|
||||||
|
SVar:DBEffect:DB$ Effect | Triggers$ TrigDies | Condition$ OptionalCost | ConditionOptionalPaid$ True
|
||||||
|
SVar:TrigDies:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature | Execute$ TrigGainLife | TriggerDescription$ Whenever a creature dies this turn, you gain 1 life.
|
||||||
|
SVar:TrigGainLife:DB$ GainLife | LifeAmount$ 1
|
||||||
|
Oracle:As an additional cost to cast this spell, you may waterbend {4}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)\nAll creatures get -2/-2 until end of turn. If this spell's additional cost was paid, whenever a creature dies this turn, you gain 1 life.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Name:Secret of Bloodbending
|
||||||
|
ManaCost:U U U U
|
||||||
|
Types:Sorcery Lesson
|
||||||
|
S:Mode$ OptionalCost | EffectZone$ All | ValidCard$ Card.Self | ValidSA$ Spell | Cost$ Waterbend<10> | Description$ As an additional cost to cast this spell, you may waterbend {10}.
|
||||||
|
A:SP$ ControlPlayer | ValidTgts$ Opponent | Condition$ OptionalCost | ConditionOptionalPaid$ False | Combat$ True | SubAbility$ ControlTurn | SpellDescription$ You control target opponent during their next combat phase. If this spell’s additional cost was paid, you control that player during their next turn instead. Exile CARDNAME.
|
||||||
|
SVar:ControlTurn:DB$ ControlPlayer | Defined$ Targeted | Condition$ OptionalCost | ConditionOptionalPaid$ True | SubAbility$ DBExile
|
||||||
|
SVar:DBExile:DB$ ChangeZone | Defined$ Self | Origin$ Stack | Destination$ Exile
|
||||||
|
SVar:X:Count$xPaid
|
||||||
|
Oracle:As an additional cost to cast this spell, you may waterbend {10}.\nYou control target opponent during their next combat phase. If this spell’s additional cost was paid, you control that player during their next turn instead. (You see all cards that player could see and make all decisions for them.)\nExile Secret of Bloodbending.
|
||||||
@@ -4,7 +4,7 @@ Types:Legendary Creature Human Pilot Ally
|
|||||||
PT:1/4
|
PT:1/4
|
||||||
K:Flying
|
K:Flying
|
||||||
T:Mode$ AttackersDeclared | AttackingPlayer$ You | ValidAttackers$ Creature.withFlying | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever one or more creatures you control with flying attack, draw a card, then discard a card. When you discard a nonland card this way, put a +1/+1 counter on target creature you control.
|
T:Mode$ AttackersDeclared | AttackingPlayer$ You | ValidAttackers$ Creature.withFlying | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever one or more creatures you control with flying attack, draw a card, then discard a card. When you discard a nonland card this way, put a +1/+1 counter on target creature you control.
|
||||||
SVar:DBDraw:DB$ Draw | SubAbility$ DBDiscard | SpellDescription$ Draw a card, then discard a card.
|
SVar:TrigDraw:DB$ Draw | SubAbility$ DBDiscard
|
||||||
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBImmediateTrig
|
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBImmediateTrig
|
||||||
SVar:DBImmediateTrig:DB$ ImmediateTrigger | ConditionDefined$ Remembered | ConditionPresent$ Card.nonLand | ConditionCompare$ GE1 | Execute$ TrigPutCounter | SubAbility$ DBCleanup | SpellDescription$ When you discard a nonland card this way, put a +1/+1 counter on target creature you control.
|
SVar:DBImmediateTrig:DB$ ImmediateTrigger | ConditionDefined$ Remembered | ConditionPresent$ Card.nonLand | ConditionCompare$ GE1 | Execute$ TrigPutCounter | SubAbility$ DBCleanup | SpellDescription$ When you discard a nonland card this way, put a +1/+1 counter on target creature you control.
|
||||||
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1
|
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Waterbender's Restoration
|
||||||
|
ManaCost:U U
|
||||||
|
Types:Instant Lesson
|
||||||
|
S:Mode$ RaiseCost | ValidCard$ Card.Self | Activator$ You | Type$ Spell | Cost$ Waterbend<X> | EffectZone$ All | Description$ As an additional cost to cast this spell, waterbend {X}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)
|
||||||
|
A:SP$ ChangeZone | ValidTgts$ Creature.YouCtrl | Announce$ X | TargetMin$ X | TargetMax$ X | Origin$ Battlefield | Destination$ Exile | TgtPrompt$ Select target creature you control | SubAbility$ DelTrig | RememberChanged$ True | SpellDescription$ Exile X target creatures you control. Return those cards to the battlefield under their owner's control at the beginning of the next end step.
|
||||||
|
SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturn | RememberObjects$ RememberedLKI | TriggerDescription$ Return exiled permanent to the battlefield. | SubAbility$ DBCleanup
|
||||||
|
SVar:TrigReturn:DB$ ChangeZone | Origin$ Exile | Destination$ Battlefield | Defined$ DelayTriggerRememberedLKI
|
||||||
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
|
SVar:X:Count$xPaid
|
||||||
|
Oracle:As an additional cost to cast this spell, waterbend {X}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)\nExile X target creatures you control. Return those cards to the battlefield under their owner's control at the beginning of the next end step.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
Name:Waterbending Lesson
|
||||||
|
ManaCost:3 U
|
||||||
|
Types:Sorcery Lesson
|
||||||
|
A:SP$ Draw | NumCards$ 3 | SubAbility$ DBDiscard | SpellDescription$ Draw three cards. Then discard a card unless you waterbend {2}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)
|
||||||
|
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose | UnlessCost$ Waterbend<2> | UnlessPayer$ You
|
||||||
|
DeckHas:Ability$Discard
|
||||||
|
Oracle:Draw three cards. Then discard a card unless you waterbend {2}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)
|
||||||
@@ -27,18 +27,33 @@ public final class InputSelectCardsForConvokeOrImprovise extends InputSelectMany
|
|||||||
private final ManaCostBeingPaid remainingCost;
|
private final ManaCostBeingPaid remainingCost;
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final CardCollectionView availableCards;
|
private final CardCollectionView availableCards;
|
||||||
private final boolean improvise;
|
private final boolean artifacts;
|
||||||
|
private final boolean creatures;
|
||||||
|
private final Integer maxSelectable;
|
||||||
private final String cardType;
|
private final String cardType;
|
||||||
private final String description;
|
private final String description;
|
||||||
|
|
||||||
public InputSelectCardsForConvokeOrImprovise(final PlayerControllerHuman controller, final Player p, final ManaCost cost, final CardCollectionView untapped, boolean impr, final SpellAbility sa) {
|
public InputSelectCardsForConvokeOrImprovise(final PlayerControllerHuman controller, final Player p, final SpellAbility sa, final ManaCost cost, final CardCollectionView untapped, boolean artifacts, boolean creatures, Integer maxReduction) {
|
||||||
super(controller, 0, Math.min(cost.getCMC(), untapped.size()), sa);
|
super(controller, 0, Math.min(cost.getCMC(), untapped.size()), sa);
|
||||||
remainingCost = new ManaCostBeingPaid(cost);
|
remainingCost = new ManaCostBeingPaid(cost);
|
||||||
player = p;
|
player = p;
|
||||||
availableCards = untapped;
|
availableCards = untapped;
|
||||||
improvise = impr;
|
this.artifacts = artifacts;
|
||||||
cardType = impr ? "artifact" : "creature";
|
this.creatures = creatures;
|
||||||
description = impr ? "Improvise" : "Convoke";
|
this.maxSelectable = maxReduction;
|
||||||
|
|
||||||
|
if (artifacts && creatures) {
|
||||||
|
cardType = "artifact or creature";
|
||||||
|
description = "Waterbend";
|
||||||
|
} else if (!artifacts && !creatures) {
|
||||||
|
throw new IllegalArgumentException("At least one of artifacts or creatures must be true");
|
||||||
|
} else if (creatures) {
|
||||||
|
cardType = "creature";
|
||||||
|
description = "Convoke";
|
||||||
|
} else {
|
||||||
|
cardType = "artifact";
|
||||||
|
description = "Improvise";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -49,6 +64,10 @@ public final class InputSelectCardsForConvokeOrImprovise extends InputSelectMany
|
|||||||
sb.append(sa.getStackDescription()).append("\n");
|
sb.append(sa.getStackDescription()).append("\n");
|
||||||
}
|
}
|
||||||
sb.append(TextUtil.concatNoSpace("Choose ", cardType, " to tap for ", description, ".\nRemaining mana cost is ", remainingCost.toString()));
|
sb.append(TextUtil.concatNoSpace("Choose ", cardType, " to tap for ", description, ".\nRemaining mana cost is ", remainingCost.toString()));
|
||||||
|
|
||||||
|
if (maxSelectable != null) {
|
||||||
|
sb.append(". You may select up to ").append(chosenCards.size() - maxSelectable).append(" more ").append(cardType).append("(s).");
|
||||||
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +85,17 @@ public final class InputSelectCardsForConvokeOrImprovise extends InputSelectMany
|
|||||||
onSelectStateChanged(card, false);
|
onSelectStateChanged(card, false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (maxSelectable != null && chosenCards.size() >= maxSelectable) {
|
||||||
|
// Should show a different message if there's a max selectable
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
byte chosenColor;
|
byte chosenColor;
|
||||||
if (improvise) {
|
if (artifacts) {
|
||||||
|
// Waterbend/Improvise can be paid with colorless mana from artifacts
|
||||||
chosenColor = ManaCostShard.COLORLESS.getColorMask();
|
chosenColor = ManaCostShard.COLORLESS.getColorMask();
|
||||||
} else {
|
} else {
|
||||||
|
// Convoke can pay color or generic mana cost from creatures
|
||||||
ColorSet colors = card.getColor();
|
ColorSet colors = card.getColor();
|
||||||
if (colors.isMulticolor()) {
|
if (colors.isMulticolor()) {
|
||||||
//if card is multicolor, strip out any colors which can't be paid towards remaining cost
|
//if card is multicolor, strip out any colors which can't be paid towards remaining cost
|
||||||
@@ -107,10 +133,6 @@ public final class InputSelectCardsForConvokeOrImprovise extends InputSelectMany
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPlayerSelected(final Player player, final ITriggerEvent triggerEvent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<Card, ManaCostShard> getConvokeMap() {
|
public Map<Card, ManaCostShard> getConvokeMap() {
|
||||||
if (hasCancelled()) {
|
if (hasCancelled()) {
|
||||||
return Maps.newHashMap();
|
return Maps.newHashMap();
|
||||||
|
|||||||
@@ -560,9 +560,7 @@ public class HumanPlay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CardCollection cardsToDelve = new CardCollection();
|
CardCollection cardsToDelve = new CardCollection();
|
||||||
if (!effect) {
|
CostAdjustment.adjust(toPay, ability, cardsToDelve, false, effect);
|
||||||
CostAdjustment.adjust(toPay, ability, cardsToDelve, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Card offering = null;
|
Card offering = null;
|
||||||
Card emerge = null;
|
Card emerge = null;
|
||||||
|
|||||||
@@ -2332,9 +2332,9 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Card, ManaCostShard> chooseCardsForConvokeOrImprovise(final SpellAbility sa, final ManaCost manaCost,
|
public Map<Card, ManaCostShard> chooseCardsForConvokeOrImprovise(final SpellAbility sa, final ManaCost manaCost,
|
||||||
final CardCollectionView untappedCards, boolean improvise) {
|
final CardCollectionView untappedCards, boolean artifacts, boolean creatures, Integer maxReduction) {
|
||||||
final InputSelectCardsForConvokeOrImprovise inp = new InputSelectCardsForConvokeOrImprovise(this, player,
|
final InputSelectCardsForConvokeOrImprovise inp = new InputSelectCardsForConvokeOrImprovise(this, player,
|
||||||
manaCost, untappedCards, improvise, sa);
|
sa, manaCost, untappedCards, artifacts, creatures, maxReduction);
|
||||||
inp.showAndWait();
|
inp.showAndWait();
|
||||||
return inp.getConvokeMap();
|
return inp.getConvokeMap();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user