mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Merge branch 'counterStringType' into 'master'
CounterType: use String inside CounterType to handle KeywordCounter See merge request core-developers/forge!2798
This commit is contained in:
@@ -464,7 +464,7 @@ public class AiAttackController {
|
||||
final CardCollectionView beastions = ai.getCardsIn(ZoneType.Battlefield, "Beastmaster Ascension");
|
||||
int minCreatures = 7;
|
||||
for (final Card beastion : beastions) {
|
||||
final int counters = beastion.getCounters(CounterType.QUEST);
|
||||
final int counters = beastion.getCounters(CounterEnumType.QUEST);
|
||||
minCreatures = Math.min(minCreatures, 7 - counters);
|
||||
}
|
||||
if (this.attackers.size() >= minCreatures) {
|
||||
@@ -1065,7 +1065,7 @@ public class AiAttackController {
|
||||
}
|
||||
}
|
||||
// if enough damage: switch to next planeswalker or player
|
||||
if (damage >= pw.getCounters(CounterType.LOYALTY)) {
|
||||
if (damage >= pw.getCounters(CounterEnumType.LOYALTY)) {
|
||||
List<Card> pwDefending = combat.getDefendingPlaneswalkers();
|
||||
boolean found = false;
|
||||
// look for next planeswalker
|
||||
@@ -1192,7 +1192,7 @@ public class AiAttackController {
|
||||
if (isWorthLessThanAllKillers || canKillAllDangerous || numberOfPossibleBlockers < 2) {
|
||||
numberOfPossibleBlockers += 1;
|
||||
if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, false)
|
||||
&& !(attacker.hasKeyword(Keyword.UNDYING) && attacker.getCounters(CounterType.P1P1) == 0)) {
|
||||
&& !(attacker.hasKeyword(Keyword.UNDYING) && attacker.getCounters(CounterEnumType.P1P1) == 0)) {
|
||||
canBeKilledByOne = true; // there is a single creature on the battlefield that can kill the creature
|
||||
// see if the defending creature is of higher or lower
|
||||
// value. We don't want to attack only to lose value
|
||||
|
||||
@@ -228,9 +228,9 @@ public class AiBlockController {
|
||||
// 3.Blockers that can destroy the attacker and have an upside when dying
|
||||
killingBlockers = getKillingBlockers(combat, attacker, blockers);
|
||||
for (Card b : killingBlockers) {
|
||||
if ((b.hasKeyword(Keyword.UNDYING) && b.getCounters(CounterType.P1P1) == 0) || b.hasSVar("SacMe")
|
||||
|| (b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterType.TIME) == 1)
|
||||
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterType.FADE) == 0)
|
||||
if ((b.hasKeyword(Keyword.UNDYING) && b.getCounters(CounterEnumType.P1P1) == 0) || b.hasSVar("SacMe")
|
||||
|| (b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterEnumType.TIME) == 1)
|
||||
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterEnumType.FADE) == 0)
|
||||
|| b.hasSVar("EndOfTurnLeavePlay")) {
|
||||
blocker = b;
|
||||
break;
|
||||
@@ -299,8 +299,8 @@ public class AiBlockController {
|
||||
final List<Card> blockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
|
||||
|
||||
for (Card b : blockers) {
|
||||
if ((b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterType.TIME) == 1)
|
||||
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterType.FADE) == 0)
|
||||
if ((b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterEnumType.TIME) == 1)
|
||||
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterEnumType.FADE) == 0)
|
||||
|| b.hasSVar("EndOfTurnLeavePlay")) {
|
||||
blocker = b;
|
||||
if (!ComputerUtilCombat.canDestroyAttacker(ai, attacker, blocker, combat, false)) {
|
||||
@@ -851,7 +851,7 @@ public class AiBlockController {
|
||||
damageToPW += ComputerUtilCombat.predictDamageTo((Card) def, pwatkr.getNetCombatDamage(), pwatkr, true);
|
||||
}
|
||||
}
|
||||
if ((!onlyIfLethal && damageToPW > 0) || damageToPW >= def.getCounters(CounterType.LOYALTY)) {
|
||||
if ((!onlyIfLethal && damageToPW > 0) || damageToPW >= def.getCounters(CounterEnumType.LOYALTY)) {
|
||||
threatenedPWs.add((Card) def);
|
||||
}
|
||||
}
|
||||
@@ -909,7 +909,7 @@ public class AiBlockController {
|
||||
damageToPW += ComputerUtilCombat.predictDamageTo(pw, pwAtk.getNetCombatDamage(), pwAtk, true);
|
||||
}
|
||||
}
|
||||
if (!isFullyBlocked && damageToPW >= pw.getCounters(CounterType.LOYALTY)) {
|
||||
if (!isFullyBlocked && damageToPW >= pw.getCounters(CounterEnumType.LOYALTY)) {
|
||||
for (Card chump : pwDefenders) {
|
||||
if (chosenChumpBlockers.contains(chump)) {
|
||||
combat.removeFromCombat(chump);
|
||||
|
||||
@@ -177,7 +177,7 @@ public class AiController {
|
||||
&& CardFactoryUtil.isCounterable(host)) {
|
||||
return true;
|
||||
} else if ("ChaliceOfTheVoid".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)
|
||||
&& host.getCMC() == c.getCounters(CounterType.CHARGE)) {
|
||||
&& host.getCMC() == c.getCounters(CounterEnumType.CHARGE)) {
|
||||
return true;
|
||||
} else if ("BazaarOfWonders".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)) {
|
||||
String hostName = host.getName();
|
||||
@@ -1801,7 +1801,7 @@ public class AiController {
|
||||
throw new UnsupportedOperationException("AI is not supposed to reach this code at the moment");
|
||||
}
|
||||
|
||||
public CardCollection chooseCardsForEffect(CardCollectionView pool, SpellAbility sa, int min, int max, boolean isOptional) {
|
||||
public CardCollection chooseCardsForEffect(CardCollectionView pool, SpellAbility sa, int min, int max, boolean isOptional, Map<String, Object> params) {
|
||||
if (sa == null || sa.getApi() == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@@ -1834,7 +1834,7 @@ public class AiController {
|
||||
default:
|
||||
CardCollection editablePool = new CardCollection(pool);
|
||||
for (int i = 0; i < max; i++) {
|
||||
Card c = player.getController().chooseSingleEntityForEffect(editablePool, sa, null, isOptional);
|
||||
Card c = player.getController().chooseSingleEntityForEffect(editablePool, sa, null, isOptional, params);
|
||||
if (c != null) {
|
||||
result.add(c);
|
||||
editablePool.remove(c);
|
||||
|
||||
@@ -16,6 +16,7 @@ import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.cost.*;
|
||||
import forge.game.player.Player;
|
||||
@@ -627,41 +628,41 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
// the first things are benefit from removing counters
|
||||
|
||||
// try to remove +1/+1 counter from undying creature
|
||||
List<Card> prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.P1P1, c),
|
||||
List<Card> prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.P1P1, c),
|
||||
CardPredicates.hasKeyword("Undying"));
|
||||
|
||||
if (!prefs.isEmpty()) {
|
||||
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterType.P1P1));
|
||||
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterEnumType.P1P1));
|
||||
PaymentDecision result = PaymentDecision.card(prefs);
|
||||
result.ct = CounterType.P1P1;
|
||||
result.ct = CounterType.get(CounterEnumType.P1P1);
|
||||
return result;
|
||||
}
|
||||
|
||||
// try to remove -1/-1 counter from persist creature
|
||||
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.M1M1, c),
|
||||
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.M1M1, c),
|
||||
CardPredicates.hasKeyword("Persist"));
|
||||
|
||||
if (!prefs.isEmpty()) {
|
||||
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterType.M1M1));
|
||||
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterEnumType.M1M1));
|
||||
PaymentDecision result = PaymentDecision.card(prefs);
|
||||
result.ct = CounterType.M1M1;
|
||||
result.ct = CounterType.get(CounterEnumType.M1M1);
|
||||
return result;
|
||||
}
|
||||
|
||||
// try to remove Time counter from Chronozoa, it will generate more
|
||||
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.TIME, c),
|
||||
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.TIME, c),
|
||||
CardPredicates.nameEquals("Chronozoa"));
|
||||
|
||||
if (!prefs.isEmpty()) {
|
||||
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterType.TIME));
|
||||
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterEnumType.TIME));
|
||||
PaymentDecision result = PaymentDecision.card(prefs);
|
||||
result.ct = CounterType.TIME;
|
||||
result.ct = CounterType.get(CounterEnumType.TIME);
|
||||
return result;
|
||||
}
|
||||
|
||||
// try to remove Quest counter on something with enough counters for the
|
||||
// effect to continue
|
||||
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.QUEST, c));
|
||||
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.QUEST, c));
|
||||
|
||||
if (!prefs.isEmpty()) {
|
||||
prefs = CardLists.filter(prefs, new Predicate<Card>() {
|
||||
@@ -673,12 +674,12 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
if (crd.hasSVar("MaxQuestEffect")) {
|
||||
e = Integer.parseInt(crd.getSVar("MaxQuestEffect"));
|
||||
}
|
||||
return crd.getCounters(CounterType.QUEST) >= e + c;
|
||||
return crd.getCounters(CounterEnumType.QUEST) >= e + c;
|
||||
}
|
||||
});
|
||||
Collections.sort(prefs, Collections.reverseOrder(CardPredicates.compareByCounterType(CounterType.QUEST)));
|
||||
Collections.sort(prefs, Collections.reverseOrder(CardPredicates.compareByCounterType(CounterEnumType.QUEST)));
|
||||
PaymentDecision result = PaymentDecision.card(prefs);
|
||||
result.ct = CounterType.QUEST;
|
||||
result.ct = CounterType.get(CounterEnumType.QUEST);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -775,7 +776,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
@Override
|
||||
public boolean apply(final Card crd) {
|
||||
for (Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
|
||||
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey() == CounterType.valueOf(ctr))) {
|
||||
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey().equals(CounterType.getType(ctr)))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -787,7 +788,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
||||
PaymentDecision result = PaymentDecision.card(card);
|
||||
|
||||
for (Map.Entry<CounterType, Integer> e : card.getCounters().entrySet()) {
|
||||
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey() == CounterType.valueOf(ctr))) {
|
||||
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey().equals(CounterType.getType(ctr)))) {
|
||||
result.ct = e.getKey();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -65,7 +65,7 @@ import java.util.*;
|
||||
* <p>
|
||||
* ComputerUtil class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -213,7 +213,7 @@ public class ComputerUtil {
|
||||
sa.setActivatingPlayer(ai);
|
||||
if (!ComputerUtilCost.canPayCost(sa, ai))
|
||||
return false;
|
||||
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||
@@ -349,8 +349,8 @@ public class ComputerUtil {
|
||||
for (int ip = 0; ip < 6; ip++) {
|
||||
final int priority = 6 - ip;
|
||||
if (priority == 2 && ai.isCardInPlay("Crucible of Worlds")) {
|
||||
CardCollection landsInPlay = CardLists.getType(typeList, "Land");
|
||||
if (!landsInPlay.isEmpty()) {
|
||||
CardCollection landsInPlay = CardLists.getType(typeList, "Land");
|
||||
if (!landsInPlay.isEmpty()) {
|
||||
// Don't need more land.
|
||||
return ComputerUtilCard.getWorstLand(landsInPlay);
|
||||
}
|
||||
@@ -379,16 +379,16 @@ public class ComputerUtil {
|
||||
return ComputerUtilCard.getWorstLand(landsInPlay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// try everything when about to die
|
||||
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
|
||||
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
|
||||
if (!nonCreatures.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(nonCreatures);
|
||||
} else if (!typeList.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(typeList);
|
||||
}
|
||||
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
|
||||
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
|
||||
if (!nonCreatures.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(nonCreatures);
|
||||
} else if (!typeList.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(typeList);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pref.contains("DiscardCost")) { // search for permanents with DiscardMe
|
||||
@@ -450,14 +450,14 @@ public class ComputerUtil {
|
||||
return ComputerUtilCard.getWorstLand(landsInHand);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// try everything when about to die
|
||||
if (activate != null && "Reality Smasher".equals(activate.getName()) ||
|
||||
game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
|
||||
if (!typeList.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(typeList);
|
||||
}
|
||||
game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
|
||||
if (!typeList.isEmpty()) {
|
||||
return ComputerUtilCard.getWorstAI(typeList);
|
||||
}
|
||||
}
|
||||
} else if (pref.contains("DonateMe")) {
|
||||
// search for permanents with DonateMe. priority 1 is the lowest, priority 5 the highest
|
||||
@@ -540,7 +540,7 @@ public class ComputerUtil {
|
||||
public static CardCollection chooseExileFrom(final Player ai, final ZoneType zone, final String type, final Card activate,
|
||||
final Card target, final int amount) {
|
||||
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null);
|
||||
|
||||
|
||||
if ((target != null) && target.getController() == ai) {
|
||||
typeList.remove(target); // don't exile the card we're pumping
|
||||
}
|
||||
@@ -561,7 +561,7 @@ public class ComputerUtil {
|
||||
public static CardCollection choosePutToLibraryFrom(final Player ai, final ZoneType zone, final String type, final Card activate,
|
||||
final Card target, final int amount) {
|
||||
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null);
|
||||
|
||||
|
||||
if ((target != null) && target.getController() == ai) {
|
||||
typeList.remove(target); // don't move the card we're pumping
|
||||
}
|
||||
@@ -572,11 +572,11 @@ public class ComputerUtil {
|
||||
|
||||
CardLists.sortByPowerAsc(typeList);
|
||||
final CardCollection list = new CardCollection();
|
||||
|
||||
|
||||
if (zone != ZoneType.Hand) {
|
||||
Collections.reverse(typeList);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
list.add(typeList.get(i));
|
||||
}
|
||||
@@ -636,7 +636,7 @@ public class ComputerUtil {
|
||||
}
|
||||
ComputerUtilCard.sortByEvaluateCreature(typeList);
|
||||
Collections.reverse(typeList);
|
||||
|
||||
|
||||
final CardCollection tapList = new CardCollection();
|
||||
|
||||
// Accumulate from "worst" creature
|
||||
@@ -709,7 +709,7 @@ public class ComputerUtil {
|
||||
return returnList;
|
||||
}
|
||||
|
||||
public static CardCollection choosePermanentsToSacrifice(final Player ai, final CardCollectionView cardlist, final int amount, final SpellAbility source,
|
||||
public static CardCollection choosePermanentsToSacrifice(final Player ai, final CardCollectionView cardlist, final int amount, final SpellAbility source,
|
||||
final boolean destroy, final boolean isOptional) {
|
||||
CardCollection remaining = new CardCollection(cardlist);
|
||||
final CardCollection sacrificed = new CardCollection();
|
||||
@@ -718,9 +718,9 @@ public class ComputerUtil {
|
||||
final int considerSacThreshold = getAIPreferenceParameter(host, "CreatureEvalThreshold");
|
||||
|
||||
if ("OpponentOnly".equals(source.getParam("AILogic"))) {
|
||||
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
|
||||
return sacrificed; // sacrifice none
|
||||
}
|
||||
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
|
||||
return sacrificed; // sacrifice none
|
||||
}
|
||||
} else if ("DesecrationDemon".equals(source.getParam("AILogic"))) {
|
||||
if (!SpecialCardAi.DesecrationDemon.considerSacrificingCreature(ai, source)) {
|
||||
return sacrificed; // don't sacrifice unless in special conditions specified by DesecrationDemon AI
|
||||
@@ -738,27 +738,27 @@ public class ComputerUtil {
|
||||
boolean removedSelf = false;
|
||||
|
||||
if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit") || considerSacLogic) {
|
||||
if (source.hasParam("Exploit")) {
|
||||
for (Trigger t : host.getTriggers()) {
|
||||
if (t.getMode() == TriggerType.Exploited) {
|
||||
final String execute = t.getParam("Execute");
|
||||
if (execute == null) {
|
||||
continue;
|
||||
}
|
||||
final SpellAbility exSA = AbilityFactory.getAbility(host.getSVar(execute), host);
|
||||
if (source.hasParam("Exploit")) {
|
||||
for (Trigger t : host.getTriggers()) {
|
||||
if (t.getMode() == TriggerType.Exploited) {
|
||||
final String execute = t.getParam("Execute");
|
||||
if (execute == null) {
|
||||
continue;
|
||||
}
|
||||
final SpellAbility exSA = AbilityFactory.getAbility(host.getSVar(execute), host);
|
||||
|
||||
exSA.setActivatingPlayer(ai);
|
||||
exSA.setTrigger(true);
|
||||
exSA.setActivatingPlayer(ai);
|
||||
exSA.setTrigger(true);
|
||||
|
||||
// Run non-mandatory trigger.
|
||||
// These checks only work if the Executing SpellAbility is an Ability_Sub.
|
||||
if ((exSA instanceof AbilitySub) && !SpellApiToAi.Converter.get(exSA.getApi()).doTriggerAI(ai, exSA, false)) {
|
||||
// AI would not run this trigger if given the chance
|
||||
return sacrificed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Run non-mandatory trigger.
|
||||
// These checks only work if the Executing SpellAbility is an Ability_Sub.
|
||||
if ((exSA instanceof AbilitySub) && !SpellApiToAi.Converter.get(exSA.getApi()).doTriggerAI(ai, exSA, false)) {
|
||||
// AI would not run this trigger if given the chance
|
||||
return sacrificed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remaining = CardLists.filter(remaining, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
@@ -819,7 +819,7 @@ public class ComputerUtil {
|
||||
if (ai.isOpponentOf(c.getController()))
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
if (destroy) {
|
||||
final CardCollection indestructibles = CardLists.getKeyword(remaining, Keyword.INDESTRUCTIBLE);
|
||||
if (!indestructibles.isEmpty()) {
|
||||
@@ -909,7 +909,7 @@ public class ComputerUtil {
|
||||
|
||||
} catch (final Exception ex) {
|
||||
throw new RuntimeException(TextUtil.concatNoSpace("There is an error in the card code for ", c.getName(), ":", ex.getMessage()), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,16 +958,16 @@ public class ComputerUtil {
|
||||
final Card card = sa.getHostCard();
|
||||
|
||||
if (card.hasSVar("PlayMain1")) {
|
||||
if (card.getSVar("PlayMain1").equals("ALWAYS") || sa.getPayCosts().hasNoManaCost()) {
|
||||
return true;
|
||||
} else if (card.getSVar("PlayMain1").equals("OPPONENTCREATURES")) {
|
||||
//Only play these main1 when the opponent has creatures (stealing and giving them haste)
|
||||
if (!ai.getOpponents().getCreaturesInPlay().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
} else if (!card.getController().getCreaturesInPlay().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (card.getSVar("PlayMain1").equals("ALWAYS") || sa.getPayCosts().hasNoManaCost()) {
|
||||
return true;
|
||||
} else if (card.getSVar("PlayMain1").equals("OPPONENTCREATURES")) {
|
||||
//Only play these main1 when the opponent has creatures (stealing and giving them haste)
|
||||
if (!ai.getOpponents().getCreaturesInPlay().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
} else if (!card.getController().getCreaturesInPlay().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// try not to cast Raid creatures in main 1 if an attack is likely
|
||||
@@ -980,7 +980,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
if (card.getManaCost().isZero()) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (card.hasKeyword(Keyword.RIOT) && ChooseGenericEffectAi.preferHasteForRiot(sa, ai)) {
|
||||
@@ -1008,9 +1008,9 @@ public class ComputerUtil {
|
||||
&& (card.hasKeyword(Keyword.HASTE) || ComputerUtil.hasACardGivingHaste(ai, true) || sa.isDash())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (card.hasKeyword(Keyword.EXALTED)) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
//cast equipments in Main1 when there are creatures to equip and no other unequipped equipment
|
||||
@@ -1140,7 +1140,7 @@ public class ComputerUtil {
|
||||
if (discard.hasSVar("DiscardMe")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
final Game game = ai.getGame();
|
||||
final CardCollection landsInPlay = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS);
|
||||
final CardCollection landsInHand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS);
|
||||
@@ -1240,11 +1240,11 @@ public class ComputerUtil {
|
||||
}
|
||||
}
|
||||
} // AntiBuffedBy
|
||||
|
||||
if (sub != null) {
|
||||
|
||||
if (sub != null) {
|
||||
return castSpellInMain1(ai, sub);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1253,7 +1253,7 @@ public class ComputerUtil {
|
||||
int activations = sa.getActivationsThisTurn();
|
||||
|
||||
if (!sa.isIntrinsic()) {
|
||||
return MyRandom.getRandom().nextFloat() >= .95; // Abilities created by static abilities have no memory
|
||||
return MyRandom.getRandom().nextFloat() >= .95; // Abilities created by static abilities have no memory
|
||||
}
|
||||
|
||||
if (activations < 10) { //10 activations per turn should still be acceptable
|
||||
@@ -1270,27 +1270,27 @@ public class ComputerUtil {
|
||||
return false;
|
||||
}
|
||||
if (abCost.hasTapCost() && source.hasSVar("AITapDown")) {
|
||||
return true;
|
||||
return true;
|
||||
} else if (sa.hasParam("Planeswalker") && ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
|
||||
for (final CostPart part : abCost.getCostParts()) {
|
||||
if (part instanceof CostPutCounter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (final CostPart part : abCost.getCostParts()) {
|
||||
if (part instanceof CostPutCounter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final CostPart part : abCost.getCostParts()) {
|
||||
if (part instanceof CostSacrifice) {
|
||||
final CostSacrifice sac = (CostSacrifice) part;
|
||||
|
||||
|
||||
final String type = sac.getType();
|
||||
|
||||
|
||||
if (type.equals("CARDNAME")) {
|
||||
if (source.getSVar("SacMe").equals("6")) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
final CardCollection typeList =
|
||||
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(","), source.getController(), source, sa);
|
||||
for (Card c : typeList) {
|
||||
@@ -1325,14 +1325,14 @@ public class ComputerUtil {
|
||||
Map<String, String> params = stAb.getMapParams();
|
||||
if ("Continuous".equals(params.get("Mode")) && params.containsKey("AddKeyword")
|
||||
&& params.get("AddKeyword").contains("Haste")) {
|
||||
|
||||
|
||||
if (c.isEquipment() && c.getEquipping() == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final String affected = params.get("Affected");
|
||||
if (affected.contains("Creature.YouCtrl")
|
||||
|| affected.contains("Other+YouCtrl")) {
|
||||
|| affected.contains("Other+YouCtrl")) {
|
||||
return true;
|
||||
} else if (affected.contains("Creature.PairedWith") && !c.isPaired()) {
|
||||
return true;
|
||||
@@ -1341,10 +1341,10 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
for (Trigger t : c.getTriggers()) {
|
||||
Map<String, String> params = t.getMapParams();
|
||||
Map<String, String> params = t.getMapParams();
|
||||
if (!"ChangesZone".equals(params.get("Mode"))
|
||||
|| !"Battlefield".equals(params.get("Destination"))
|
||||
|| !params.containsKey("ValidCard")) {
|
||||
|| !"Battlefield".equals(params.get("Destination"))
|
||||
|| !params.containsKey("ValidCard")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1360,10 +1360,10 @@ public class ComputerUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
all.addAll(ai.getCardsActivableInExternalZones(true));
|
||||
all.addAll(ai.getCardsIn(ZoneType.Hand));
|
||||
|
||||
|
||||
for (final Card c : all) {
|
||||
for (final SpellAbility sa : c.getSpellAbilities()) {
|
||||
if (sa.getApi() == ApiType.Pump && sa.hasParam("KW") && sa.getParam("KW").contains("Haste")) {
|
||||
@@ -1398,10 +1398,10 @@ public class ComputerUtil {
|
||||
|
||||
public static boolean hasAFogEffect(final Player ai) {
|
||||
final CardCollection all = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
|
||||
|
||||
|
||||
all.addAll(ai.getCardsActivableInExternalZones(true));
|
||||
all.addAll(ai.getCardsIn(ZoneType.Hand));
|
||||
|
||||
|
||||
for (final Card c : all) {
|
||||
for (final SpellAbility sa : c.getSpellAbilities()) {
|
||||
if (sa.getApi() != ApiType.Fog) {
|
||||
@@ -1431,7 +1431,7 @@ public class ComputerUtil {
|
||||
final CardCollection all = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
|
||||
all.addAll(ai.getCardsActivableInExternalZones(true));
|
||||
all.addAll(CardLists.filter(ai.getCardsIn(ZoneType.Hand), Predicates.not(Presets.PERMANENTS)));
|
||||
|
||||
|
||||
for (final Card c : all) {
|
||||
for (final SpellAbility sa : c.getSpellAbilities()) {
|
||||
if (sa.getApi() != ApiType.DealDamage) {
|
||||
@@ -1490,7 +1490,7 @@ public class ComputerUtil {
|
||||
|
||||
/**
|
||||
* Returns list of objects threatened by effects on the stack
|
||||
*
|
||||
*
|
||||
* @param ai
|
||||
* calling player
|
||||
* @param sa
|
||||
@@ -1505,7 +1505,7 @@ public class ComputerUtil {
|
||||
if (game.getStack().isEmpty()) {
|
||||
return objects;
|
||||
}
|
||||
|
||||
|
||||
// check stack for something that will kill this
|
||||
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||
// iterate from top of stack to find SpellAbility, including sub-abilities,
|
||||
@@ -1523,8 +1523,8 @@ public class ComputerUtil {
|
||||
if (top) {
|
||||
break; // only evaluate top-stack
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
@@ -1536,14 +1536,14 @@ public class ComputerUtil {
|
||||
int toughness = 0;
|
||||
boolean grantIndestructible = false;
|
||||
boolean grantShroud = false;
|
||||
|
||||
|
||||
if (topStack == null) {
|
||||
return objects;
|
||||
}
|
||||
|
||||
|
||||
final Card source = topStack.getHostCard();
|
||||
final ApiType threatApi = topStack.getApi();
|
||||
|
||||
|
||||
// Can only Predict things from AFs
|
||||
if (threatApi == null) {
|
||||
return threatened;
|
||||
@@ -1557,7 +1557,7 @@ public class ComputerUtil {
|
||||
CardCollectionView battleField = aiPlayer.getCardsIn(ZoneType.Battlefield);
|
||||
objects = CardLists.getValidCards(battleField, topStack.getParam("ValidCards").split(","), source.getController(), source, topStack);
|
||||
} else {
|
||||
return threatened;
|
||||
return threatened;
|
||||
}
|
||||
} else {
|
||||
objects = topStack.getTargets().getTargets();
|
||||
@@ -1571,7 +1571,7 @@ public class ComputerUtil {
|
||||
}
|
||||
}
|
||||
if (canBeTargeted.isEmpty()) {
|
||||
return threatened;
|
||||
return threatened;
|
||||
}
|
||||
objects = canBeTargeted;
|
||||
}
|
||||
@@ -1640,7 +1640,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
// don't use it on creatures that can't be regenerated
|
||||
if ((saviourApi == ApiType.Regenerate || saviourApi == ApiType.RegenerateAll) &&
|
||||
if ((saviourApi == ApiType.Regenerate || saviourApi == ApiType.RegenerateAll) &&
|
||||
(!c.canBeShielded() || noRegen)) {
|
||||
continue;
|
||||
}
|
||||
@@ -1652,14 +1652,14 @@ public class ComputerUtil {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll) {
|
||||
boolean canSave = ComputerUtilCombat.predictDamageTo(c, dmg - toughness, source, false) < ComputerUtilCombat.getDamageToKill(c);
|
||||
if (!canSave) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// cannot protect against source
|
||||
if (saviourApi == ApiType.Protection && (ProtectAi.toProtectFrom(source, saviour) == null)) {
|
||||
continue;
|
||||
@@ -1670,7 +1670,7 @@ public class ComputerUtil {
|
||||
if (saviourApi == ApiType.ChangeZone && (c.getOwner().isOpponentOf(aiPlayer) || c.isToken())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c)) {
|
||||
threatened.add(c);
|
||||
}
|
||||
@@ -1689,7 +1689,7 @@ public class ComputerUtil {
|
||||
}
|
||||
// -Toughness Curse
|
||||
else if ((threatApi == ApiType.Pump || threatApi == ApiType.PumpAll && topStack.isCurse())
|
||||
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|
||||
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|
||||
|| saviourApi == ApiType.Protection || saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll
|
||||
|| saviourApi == null)) {
|
||||
final int dmg = -AbilityUtils.calculateAmount(topStack.getHostCard(),
|
||||
@@ -1702,7 +1702,7 @@ public class ComputerUtil {
|
||||
if (!canRemove) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) {
|
||||
final boolean cantSave = c.getNetToughness() + toughness <= dmg
|
||||
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && !grantIndestructible
|
||||
@@ -1711,14 +1711,14 @@ public class ComputerUtil {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll) {
|
||||
boolean canSave = c.getNetToughness() + toughness > dmg;
|
||||
if (!canSave) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (saviourApi == ApiType.Protection) {
|
||||
if (tgt == null || (ProtectAi.toProtectFrom(source, saviour) == null)) {
|
||||
continue;
|
||||
@@ -1812,9 +1812,9 @@ public class ComputerUtil {
|
||||
}
|
||||
}
|
||||
//GainControl
|
||||
else if ((threatApi == ApiType.GainControl
|
||||
|| (threatApi == ApiType.Attach && topStack.hasParam("AILogic") && topStack.getParam("AILogic").equals("GainControl") ))
|
||||
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|
||||
else if ((threatApi == ApiType.GainControl
|
||||
|| (threatApi == ApiType.Attach && topStack.hasParam("AILogic") && topStack.getParam("AILogic").equals("GainControl") ))
|
||||
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|
||||
|| saviourApi == ApiType.Protection || saviourApi == null)) {
|
||||
for (final Object o : objects) {
|
||||
if (o instanceof Card) {
|
||||
@@ -1931,7 +1931,7 @@ public class ComputerUtil {
|
||||
public static int scoreHand(CardCollectionView handList, Player ai, int cardsToReturn) {
|
||||
// TODO Improve hand scoring in relation to cards to return.
|
||||
// If final hand size is 5, score a hand based on what that 5 would be.
|
||||
// Or if this is really really fast, determine what the 5 would be based on scoring
|
||||
// Or if this is really really fast, determine what the 5 would be based on scoring
|
||||
// All of the possibilities
|
||||
|
||||
final AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||
@@ -2014,16 +2014,16 @@ public class ComputerUtil {
|
||||
final CardCollectionView handList = ai.getCardsIn(ZoneType.Hand);
|
||||
return scoreHand(handList, ai, cardsToReturn) <= 0;
|
||||
}
|
||||
|
||||
|
||||
public static CardCollection getPartialParisCandidates(Player ai) {
|
||||
// Commander no longer uses partial paris.
|
||||
final CardCollection candidates = new CardCollection();
|
||||
final CardCollectionView handList = ai.getCardsIn(ZoneType.Hand);
|
||||
|
||||
|
||||
final CardCollection lands = CardLists.getValidCards(handList, "Card.Land", ai, null);
|
||||
final CardCollection nonLands = CardLists.getValidCards(handList, "Card.nonLand", ai, null);
|
||||
CardLists.sortByCmcDesc(nonLands);
|
||||
|
||||
|
||||
if (lands.size() >= 3 && lands.size() <= 4) {
|
||||
return candidates;
|
||||
}
|
||||
@@ -2031,7 +2031,7 @@ public class ComputerUtil {
|
||||
//Not enough lands!
|
||||
int tgtCandidates = Math.max(Math.abs(lands.size()-nonLands.size()), 3);
|
||||
System.out.println("Partial Paris: " + ai.getName() + " lacks lands, aiming to exile " + tgtCandidates + " cards.");
|
||||
|
||||
|
||||
for (int i=0;i<tgtCandidates;i++) {
|
||||
candidates.add(nonLands.get(i));
|
||||
}
|
||||
@@ -2053,7 +2053,7 @@ public class ComputerUtil {
|
||||
numProducers.get(col).add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2062,7 +2062,7 @@ public class ComputerUtil {
|
||||
System.out.print(c.toString() + ", ");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
|
||||
if (candidates.size() < 2) {
|
||||
candidates.clear();
|
||||
}
|
||||
@@ -2102,7 +2102,7 @@ public class ComputerUtil {
|
||||
CardCollection landsInHand = CardLists.filter(cardsInHand, CardPredicates.Presets.LANDS_PRODUCING_MANA);
|
||||
// valuable mana-producing artifacts that may be equated to a land
|
||||
List<String> manaArts = Arrays.asList("Mox Pearl", "Mox Sapphire", "Mox Jet", "Mox Ruby", "Mox Emerald");
|
||||
|
||||
|
||||
// evaluate creatures available in deck
|
||||
CardCollectionView allCreatures = CardLists.filter(allCards, Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.isOwner(player)));
|
||||
int numCards = allCreatures.size();
|
||||
@@ -2185,7 +2185,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
Collections.sort(goodChoices, CardLists.TextLenComparator);
|
||||
|
||||
|
||||
CardLists.sortByCmcDesc(goodChoices);
|
||||
dChoices.add(goodChoices.get(0));
|
||||
|
||||
@@ -2196,7 +2196,7 @@ public class ComputerUtil {
|
||||
if (p == aiChooser) { // ask that ai player what he would like to discard
|
||||
final AiController aic = ((PlayerControllerAi)p.getController()).getAi();
|
||||
return aic.getCardsToDiscard(min, max, validCards, sa);
|
||||
}
|
||||
}
|
||||
// no special options for human or remote friends
|
||||
return getCardsToDiscardFromOpponent(aiChooser, p, sa, validCards, min, max);
|
||||
}
|
||||
@@ -2245,7 +2245,7 @@ public class ComputerUtil {
|
||||
chosen = ComputerUtilCard.getMostProminentType(ai.getCardsIn(ZoneType.Battlefield), valid);
|
||||
}
|
||||
else if (logic.equals("MostProminentOppControls")) {
|
||||
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
|
||||
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
|
||||
chosen = ComputerUtilCard.getMostProminentType(list, valid);
|
||||
if (!CardType.isACreatureType(chosen) || invalidTypes.contains(chosen)) {
|
||||
list = CardLists.filterControlledBy(game.getCardsInGame(), ai.getOpponents());
|
||||
@@ -2272,11 +2272,11 @@ public class ComputerUtil {
|
||||
|
||||
chosen = ComputerUtilCard.getMostProminentType(list, valid);
|
||||
} else if (logic.equals("MostNeededType")) {
|
||||
// Choose a type that is in the deck, but not in hand or on the battlefield
|
||||
// Choose a type that is in the deck, but not in hand or on the battlefield
|
||||
final List<String> basics = new ArrayList<>(CardType.Constant.BASIC_TYPES);
|
||||
CardCollectionView presentCards = CardCollection.combine(ai.getCardsIn(ZoneType.Battlefield), ai.getCardsIn(ZoneType.Hand));
|
||||
CardCollectionView possibleCards = ai.getAllCards();
|
||||
|
||||
|
||||
for (String b : basics) {
|
||||
if (!Iterables.any(presentCards, CardPredicates.isType(b)) && Iterables.any(possibleCards, CardPredicates.isType(b))) {
|
||||
chosen = b;
|
||||
@@ -2333,6 +2333,8 @@ public class ComputerUtil {
|
||||
|
||||
boolean opponent = controller.isOpponentOf(ai);
|
||||
|
||||
final CounterType p1p1Type = CounterType.get(CounterEnumType.P1P1);
|
||||
|
||||
if (!sa.hasParam("AILogic")) {
|
||||
return Aggregates.random(options);
|
||||
}
|
||||
@@ -2386,7 +2388,7 @@ public class ComputerUtil {
|
||||
}
|
||||
}
|
||||
// is it can't receive counters, choose +1/+1 ones
|
||||
if (!source.canReceiveCounters(CounterType.P1P1)) {
|
||||
if (!source.canReceiveCounters(p1p1Type)) {
|
||||
return opponent ? "Feather" : "Quill";
|
||||
}
|
||||
// if source is not on the battlefield anymore, choose +1/+1
|
||||
@@ -2418,7 +2420,7 @@ public class ComputerUtil {
|
||||
Card token = TokenAi.spawnToken(controller, saToken);
|
||||
|
||||
// is it can't receive counters, choose +1/+1 ones
|
||||
if (!source.canReceiveCounters(CounterType.P1P1)) {
|
||||
if (!source.canReceiveCounters(p1p1Type)) {
|
||||
return opponent ? "Strength" : "Numbers";
|
||||
}
|
||||
|
||||
@@ -2441,11 +2443,11 @@ public class ComputerUtil {
|
||||
Card sourceNumbers = CardUtil.getLKICopy(source);
|
||||
Card sourceStrength = CardUtil.getLKICopy(source);
|
||||
|
||||
sourceNumbers.setCounters(CounterType.P1P1, sourceNumbers.getCounters(CounterType.P1P1) + numStrength);
|
||||
sourceNumbers.setCounters(p1p1Type, sourceNumbers.getCounters(p1p1Type) + numStrength);
|
||||
sourceNumbers.setZone(source.getZone());
|
||||
|
||||
sourceStrength.setCounters(CounterType.P1P1,
|
||||
sourceStrength.getCounters(CounterType.P1P1) + numStrength + 1);
|
||||
sourceStrength.setCounters(p1p1Type,
|
||||
sourceStrength.getCounters(p1p1Type) + numStrength + 1);
|
||||
sourceStrength.setZone(source.getZone());
|
||||
|
||||
int scoreStrength = ComputerUtilCard.evaluateCreature(sourceStrength) + tokenScore * numNumbers;
|
||||
@@ -2467,7 +2469,7 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
// is it can't receive counters, choose +1/+1 ones
|
||||
if (!source.canReceiveCounters(CounterType.P1P1)) {
|
||||
if (!source.canReceiveCounters(p1p1Type)) {
|
||||
return opponent ? "Sprout" : "Harvest";
|
||||
}
|
||||
|
||||
@@ -2544,11 +2546,11 @@ public class ComputerUtil {
|
||||
});
|
||||
return ComputerUtilCard.getBestCreatureAI(killables);
|
||||
}
|
||||
|
||||
|
||||
public static int predictDamageFromSpell(final SpellAbility sa, final Player targetPlayer) {
|
||||
int damage = -1; // returns -1 if the spell does not deal damage
|
||||
final Card card = sa.getHostCard();
|
||||
|
||||
|
||||
SpellAbility ab = sa;
|
||||
while (ab != null) {
|
||||
if (ab.getApi() == ApiType.DealDamage) {
|
||||
@@ -2567,12 +2569,12 @@ public class ComputerUtil {
|
||||
}
|
||||
ab = ab.getSubAbility();
|
||||
}
|
||||
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
||||
public static int getDamageForPlaying(final Player player, final SpellAbility sa) {
|
||||
|
||||
|
||||
// check for bad spell cast triggers
|
||||
int damage = 0;
|
||||
final Game game = player.getGame();
|
||||
@@ -2602,7 +2604,7 @@ public class ComputerUtil {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (trigParams.containsKey("ValidActivatingPlayer")) {
|
||||
if (!player.isValid(trigParams.get("ValidActivatingPlayer"), source.getController(), source, sa)) {
|
||||
continue;
|
||||
@@ -2662,7 +2664,7 @@ public class ComputerUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
@@ -2685,7 +2687,7 @@ public class ComputerUtil {
|
||||
if (!trigger.requirementsCheck(game)) {
|
||||
continue;
|
||||
}
|
||||
if (trigParams.containsKey("CheckOnTriggeredCard")
|
||||
if (trigParams.containsKey("CheckOnTriggeredCard")
|
||||
&& AbilityUtils.getDefinedCards(permanent, source.getSVar(trigParams.get("CheckOnTriggeredCard").split(" ")[0]), null).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -2759,27 +2761,27 @@ public class ComputerUtil {
|
||||
}
|
||||
|
||||
public static boolean isNegativeCounter(CounterType type, Card c) {
|
||||
return type == CounterType.AGE || type == CounterType.BRIBERY || type == CounterType.DOOM
|
||||
|| type == CounterType.M1M1 || type == CounterType.M0M2 || type == CounterType.M0M1
|
||||
|| type == CounterType.M1M0 || type == CounterType.M2M1 || type == CounterType.M2M2
|
||||
return type.is(CounterEnumType.AGE) || type.is(CounterEnumType.BRIBERY) || type.is(CounterEnumType.DOOM)
|
||||
|| type.is(CounterEnumType.M1M1) || type.is(CounterEnumType.M0M2) || type.is(CounterEnumType.M0M1)
|
||||
|| type.is(CounterEnumType.M1M0) || type.is(CounterEnumType.M2M1) || type.is(CounterEnumType.M2M2)
|
||||
// Blaze only hurts Lands
|
||||
|| (type == CounterType.BLAZE && c.isLand())
|
||||
|| (type.is(CounterEnumType.BLAZE) && c.isLand())
|
||||
// Iceberg does use Ice as Storage
|
||||
|| (type == CounterType.ICE && !"Iceberg".equals(c.getName()))
|
||||
|| (type.is(CounterEnumType.ICE) && !"Iceberg".equals(c.getName()))
|
||||
// some lands does use Depletion as Storage Counter
|
||||
|| (type == CounterType.DEPLETION && c.hasKeyword("CARDNAME doesn't untap during your untap step."))
|
||||
|| (type.is(CounterEnumType.DEPLETION) && c.hasKeyword("CARDNAME doesn't untap during your untap step."))
|
||||
// treat Time Counters on suspended Cards as Bad,
|
||||
// and also on Chronozoa
|
||||
|| (type == CounterType.TIME && (!c.isInPlay() || "Chronozoa".equals(c.getName())))
|
||||
|| type == CounterType.GOLD || type == CounterType.MUSIC || type == CounterType.PUPA
|
||||
|| type == CounterType.PARALYZATION || type == CounterType.SHELL || type == CounterType.SLEEP
|
||||
|| type == CounterType.SLUMBER || type == CounterType.SLEIGHT || type == CounterType.WAGE;
|
||||
|| (type.is(CounterEnumType.TIME) && (!c.isInPlay() || "Chronozoa".equals(c.getName())))
|
||||
|| type.is(CounterEnumType.GOLD) || type.is(CounterEnumType.MUSIC) || type.is(CounterEnumType.PUPA)
|
||||
|| type.is(CounterEnumType.PARALYZATION) || type.is(CounterEnumType.SHELL) || type.is(CounterEnumType.SLEEP)
|
||||
|| type.is(CounterEnumType.SLUMBER) || type.is(CounterEnumType.SLEIGHT) || type.is(CounterEnumType.WAGE);
|
||||
}
|
||||
|
||||
// this countertypes has no effect
|
||||
public static boolean isUselessCounter(CounterType type) {
|
||||
return type == CounterType.AWAKENING || type == CounterType.MANIFESTATION || type == CounterType.PETRIFICATION
|
||||
|| type == CounterType.TRAINING;
|
||||
return type.is(CounterEnumType.AWAKENING) || type.is(CounterEnumType.MANIFESTATION) || type.is(CounterEnumType.PETRIFICATION)
|
||||
|| type.is(CounterEnumType.TRAINING);
|
||||
}
|
||||
|
||||
public static Player evaluateBoardPosition(final List<Player> listToEvaluate) {
|
||||
@@ -2891,7 +2893,7 @@ public class ComputerUtil {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static boolean targetPlayableSpellCard(final Player ai, CardCollection options, final SpellAbility sa, final boolean withoutPayingManaCost) {
|
||||
// determine and target a card with a SA that the AI can afford and will play
|
||||
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||
|
||||
@@ -1422,8 +1422,8 @@ public class ComputerUtilCard {
|
||||
if (combat.isAttacking(c) && opp.getLife() > 0) {
|
||||
int dmg = ComputerUtilCombat.damageIfUnblocked(c, opp, combat, true);
|
||||
int pumpedDmg = ComputerUtilCombat.damageIfUnblocked(pumped, opp, pumpedCombat, true);
|
||||
int poisonOrig = opp.canReceiveCounters(CounterType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(c, ai) : 0;
|
||||
int poisonPumped = opp.canReceiveCounters(CounterType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(pumped, ai) : 0;
|
||||
int poisonOrig = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(c, ai) : 0;
|
||||
int poisonPumped = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(pumped, ai) : 0;
|
||||
|
||||
// predict Infect
|
||||
if (pumpedDmg == 0 && c.hasKeyword(Keyword.INFECT)) {
|
||||
@@ -1446,7 +1446,7 @@ public class ComputerUtilCard {
|
||||
}
|
||||
if (pumpedDmg > dmg) {
|
||||
if ((!c.hasKeyword(Keyword.INFECT) && pumpedDmg >= opp.getLife())
|
||||
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterType.POISON) && pumpedDmg >= opp.getPoisonCounters())
|
||||
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterEnumType.POISON) && pumpedDmg >= opp.getPoisonCounters())
|
||||
|| ("PumpForTrample".equals(sa.getParam("AILogic")))) {
|
||||
return true;
|
||||
}
|
||||
@@ -1765,10 +1765,10 @@ public class ComputerUtilCard {
|
||||
}
|
||||
|
||||
public static boolean hasActiveUndyingOrPersist(final Card c) {
|
||||
if (c.hasKeyword(Keyword.UNDYING) && c.getCounters(CounterType.P1P1) == 0) {
|
||||
if (c.hasKeyword(Keyword.UNDYING) && c.getCounters(CounterEnumType.P1P1) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (c.hasKeyword(Keyword.PERSIST) && c.getCounters(CounterType.M1M1) == 0) {
|
||||
if (c.hasKeyword(Keyword.PERSIST) && c.getCounters(CounterEnumType.M1M1) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -328,7 +328,7 @@ public class ComputerUtilCombat {
|
||||
public static int resultingPoison(final Player ai, final Combat combat) {
|
||||
|
||||
// ai can't get poision counters, so the value can't change
|
||||
if (!ai.canReceiveCounters(CounterType.POISON)) {
|
||||
if (!ai.canReceiveCounters(CounterEnumType.POISON)) {
|
||||
return ai.getPoisonCounters();
|
||||
}
|
||||
|
||||
@@ -931,7 +931,7 @@ public class ComputerUtilCombat {
|
||||
if (dealsFirstStrikeDamage(attacker, withoutAbilities, null)
|
||||
&& (attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT))
|
||||
&& !dealsFirstStrikeDamage(blocker, withoutAbilities, null)
|
||||
&& !blocker.canReceiveCounters(CounterType.M1M1)) {
|
||||
&& !blocker.canReceiveCounters(CounterEnumType.M1M1)) {
|
||||
power -= attacker.getNetCombatDamage();
|
||||
}
|
||||
|
||||
@@ -1058,7 +1058,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ability.hasParam("Adapt") && blocker.getCounters(CounterType.P1P1) > 0) {
|
||||
if (ability.hasParam("Adapt") && blocker.getCounters(CounterEnumType.P1P1) > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1234,7 +1234,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ability.hasParam("Adapt") && blocker.getCounters(CounterType.P1P1) > 0) {
|
||||
if (ability.hasParam("Adapt") && blocker.getCounters(CounterEnumType.P1P1) > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1296,7 +1296,7 @@ public class ComputerUtilCombat {
|
||||
if (ComputerUtilCombat.dealsFirstStrikeDamage(blocker, withoutAbilities, combat)
|
||||
&& (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT))
|
||||
&& !ComputerUtilCombat.dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
|
||||
&& !attacker.canReceiveCounters(CounterType.M1M1)) {
|
||||
&& !attacker.canReceiveCounters(CounterEnumType.M1M1)) {
|
||||
power -= blocker.getNetCombatDamage();
|
||||
}
|
||||
theTriggers.addAll(blocker.getTriggers());
|
||||
@@ -1456,7 +1456,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
|
||||
if (ability.hasParam("Adapt") && attacker.getCounters(CounterEnumType.P1P1) > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1693,7 +1693,7 @@ public class ComputerUtilCombat {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
|
||||
if (ability.hasParam("Adapt") && attacker.getCounters(CounterEnumType.P1P1) > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1848,10 +1848,10 @@ public class ComputerUtilCombat {
|
||||
|
||||
if (((attacker.hasKeyword(Keyword.INDESTRUCTIBLE) || (ComputerUtil.canRegenerate(ai, attacker) && !withoutAbilities))
|
||||
&& !(blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT)))
|
||||
|| (attacker.hasKeyword(Keyword.PERSIST) && !attacker.canReceiveCounters(CounterType.M1M1) && (attacker
|
||||
.getCounters(CounterType.M1M1) == 0))
|
||||
|| (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterType.P1P1) && (attacker
|
||||
.getCounters(CounterType.P1P1) == 0))) {
|
||||
|| (attacker.hasKeyword(Keyword.PERSIST) && !attacker.canReceiveCounters(CounterEnumType.M1M1) && (attacker
|
||||
.getCounters(CounterEnumType.M1M1) == 0))
|
||||
|| (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterEnumType.P1P1) && (attacker
|
||||
.getCounters(CounterEnumType.P1P1) == 0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2080,10 +2080,10 @@ public class ComputerUtilCombat {
|
||||
|
||||
if (((blocker.hasKeyword(Keyword.INDESTRUCTIBLE) || (ComputerUtil.canRegenerate(ai, blocker) && !withoutAbilities)) && !(attacker
|
||||
.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT)))
|
||||
|| (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterType.M1M1) && (blocker
|
||||
.getCounters(CounterType.M1M1) == 0))
|
||||
|| (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterType.P1P1) && (blocker
|
||||
.getCounters(CounterType.P1P1) == 0))) {
|
||||
|| (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterEnumType.M1M1) && (blocker
|
||||
.getCounters(CounterEnumType.M1M1) == 0))
|
||||
|| (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterEnumType.P1P1) && (blocker
|
||||
.getCounters(CounterEnumType.P1P1) == 0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public class ComputerUtilCost {
|
||||
final CostPutCounter addCounter = (CostPutCounter) part;
|
||||
final CounterType type = addCounter.getCounter();
|
||||
|
||||
if (type.equals(CounterType.M1M1)) {
|
||||
if (type.equals(CounterEnumType.M1M1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public class ComputerUtilCost {
|
||||
|
||||
final CounterType type = remCounter.counter;
|
||||
if (!part.payCostFromSource()) {
|
||||
if (CounterType.P1P1.equals(type)) {
|
||||
if (CounterEnumType.P1P1.equals(type)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@@ -97,7 +97,7 @@ public class ComputerUtilCost {
|
||||
|
||||
// check the sa what the PaymentDecision is.
|
||||
// ignore Loyality abilities with Zero as Cost
|
||||
if (sa != null && !CounterType.LOYALTY.equals(type)) {
|
||||
if (sa != null && !CounterEnumType.LOYALTY.equals(type)) {
|
||||
final AiCostDecision decision = new AiCostDecision(sa.getActivatingPlayer(), sa);
|
||||
PaymentDecision pay = decision.visit(remCounter);
|
||||
if (pay == null || pay.c <= 0) {
|
||||
@@ -106,7 +106,7 @@ public class ComputerUtilCost {
|
||||
}
|
||||
|
||||
//don't kill the creature
|
||||
if (CounterType.P1P1.equals(type) && source.getLethalDamage() <= 1
|
||||
if (CounterEnumType.P1P1.equals(type) && source.getLethalDamage() <= 1
|
||||
&& !source.hasKeyword(Keyword.UNDYING)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ public class ComputerUtilMana {
|
||||
adjustManaCostToAvoidNegEffects(cost, sa.getHostCard(), ai);
|
||||
List<Mana> manaSpentToPay = test ? new ArrayList<>() : sa.getPayingMana();
|
||||
boolean purePhyrexian = cost.containsOnlyPhyrexianMana();
|
||||
int testEnergyPool = ai.getCounters(CounterType.ENERGY);
|
||||
int testEnergyPool = ai.getCounters(CounterEnumType.ENERGY);
|
||||
|
||||
List<SpellAbility> paymentList = Lists.newArrayList();
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.google.common.base.Function;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.cost.CostPayEnergy;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
@@ -246,7 +246,7 @@ public class CreatureEvaluator implements Function<Card, Integer> {
|
||||
// Electrostatic Pummeler, can be expanded for similar cards
|
||||
int initPower = getEffectivePower(sa.getHostCard());
|
||||
int pumpedPower = initPower;
|
||||
int energy = sa.getHostCard().getController().getCounters(CounterType.ENERGY);
|
||||
int energy = sa.getHostCard().getController().getCounters(CounterEnumType.ENERGY);
|
||||
if (energy > 0) {
|
||||
int numActivations = energy / 3;
|
||||
for (int i = 0; i < numActivations; i++) {
|
||||
|
||||
@@ -1091,11 +1091,11 @@ public abstract class GameState {
|
||||
}
|
||||
|
||||
private void applyCountersToGameEntity(GameEntity entity, String counterString) {
|
||||
entity.setCounters(Maps.newEnumMap(CounterType.class));
|
||||
entity.setCounters(Maps.newHashMap());
|
||||
String[] allCounterStrings = counterString.split(",");
|
||||
for (final String counterPair : allCounterStrings) {
|
||||
String[] pair = counterPair.split("=", 2);
|
||||
entity.addCounter(CounterType.valueOf(pair[0]), Integer.parseInt(pair[1]), null, false, false, null);
|
||||
entity.addCounter(CounterType.getType(pair[0]), Integer.parseInt(pair[1]), null, false, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1137,7 +1137,7 @@ public abstract class GameState {
|
||||
Map<CounterType, Integer> counters = c.getCounters();
|
||||
// Note: Not clearCounters() since we want to keep the counters
|
||||
// var as-is.
|
||||
c.setCounters(Maps.newEnumMap(CounterType.class));
|
||||
c.setCounters(Maps.newHashMap());
|
||||
if (c.isAura()) {
|
||||
// dummy "enchanting" to indicate that the card will be force-attached elsewhere
|
||||
// (will be overridden later, so the actual value shouldn't matter)
|
||||
|
||||
@@ -145,12 +145,12 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional) {
|
||||
return brains.chooseCardsForEffect(sourceList, sa, min, max, isOptional);
|
||||
public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional, Map<String, Object> params) {
|
||||
return brains.chooseCardsForEffect(sourceList, sa, min, max, isOptional, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) {
|
||||
public <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
if (delayedReveal != null) {
|
||||
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
|
||||
}
|
||||
@@ -158,13 +158,13 @@ public class PlayerControllerAi extends PlayerController {
|
||||
if (null == api) {
|
||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
||||
}
|
||||
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer);
|
||||
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GameEntity> List<T> chooseEntitiesForEffect(
|
||||
FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title,
|
||||
Player targetedPlayer) {
|
||||
Player targetedPlayer, Map<String, Object> params) {
|
||||
if (delayedReveal != null) {
|
||||
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
|
||||
}
|
||||
@@ -172,7 +172,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
List<T> selecteds = new ArrayList<>();
|
||||
T selected;
|
||||
do {
|
||||
selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer);
|
||||
selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer, params);
|
||||
if ( selected != null ) {
|
||||
remaining.remove(selected);
|
||||
selecteds.add(selected);
|
||||
@@ -182,7 +182,23 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title,
|
||||
public List<SpellAbility> chooseSpellAbilitiesForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
|
||||
int num, Map<String, Object> params) {
|
||||
List<SpellAbility> remaining = Lists.newArrayList(spells);
|
||||
List<SpellAbility> selecteds = Lists.newArrayList();
|
||||
SpellAbility selected;
|
||||
do {
|
||||
selected = chooseSingleSpellForEffect(remaining, sa, title, params);
|
||||
if ( selected != null ) {
|
||||
remaining.remove(selected);
|
||||
selecteds.add(selected);
|
||||
}
|
||||
} while ( (selected != null ) && (selecteds.size() < num) );
|
||||
return selecteds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
|
||||
Map<String, Object> params) {
|
||||
ApiType api = sa.getApi();
|
||||
if (null == api) {
|
||||
|
||||
@@ -327,7 +327,7 @@ public class SpecialCardAi {
|
||||
boolean canTrample = source.hasKeyword(Keyword.TRAMPLE);
|
||||
|
||||
if (!isBlocking && combat.getDefenderByAttacker(source) instanceof Card) {
|
||||
int loyalty = combat.getDefenderByAttacker(source).getCounters(CounterType.LOYALTY);
|
||||
int loyalty = combat.getDefenderByAttacker(source).getCounters(CounterEnumType.LOYALTY);
|
||||
int totalDamageToPW = 0;
|
||||
for (Card atk : (combat.getAttackersOf(combat.getDefenderByAttacker(source)))) {
|
||||
if (combat.isUnblocked(atk)) {
|
||||
@@ -407,7 +407,7 @@ public class SpecialCardAi {
|
||||
}
|
||||
|
||||
public static Pair<Integer, Integer> getPumpedPT(Player ai, int power, int toughness) {
|
||||
int energy = ai.getCounters(CounterType.ENERGY);
|
||||
int energy = ai.getCounters(CounterEnumType.ENERGY);
|
||||
if (energy > 0) {
|
||||
int numActivations = energy / 3;
|
||||
for (int i = 0; i < numActivations; i++) {
|
||||
@@ -708,7 +708,7 @@ public class SpecialCardAi {
|
||||
// if there's another reanimator card currently suspended, don't cast a new one until the previous
|
||||
// one resolves, otherwise the reanimation attempt will be ruined (e.g. Living End)
|
||||
for (Card ex : ai.getCardsIn(ZoneType.Exile)) {
|
||||
if (ex.hasSVar("IsReanimatorCard") && ex.getCounters(CounterType.TIME) > 0) {
|
||||
if (ex.hasSVar("IsReanimatorCard") && ex.getCounters(CounterEnumType.TIME) > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -767,7 +767,7 @@ public class SpecialCardAi {
|
||||
Player controller = c.getController();
|
||||
boolean wasCaged = false;
|
||||
for (Card caged : CardLists.filter(controller.getCardsIn(ZoneType.Exile),
|
||||
CardPredicates.hasCounter(CounterType.CAGE))) {
|
||||
CardPredicates.hasCounter(CounterEnumType.CAGE))) {
|
||||
if (c.getName().equals(caged.getName())) {
|
||||
wasCaged = true;
|
||||
break;
|
||||
@@ -1073,7 +1073,7 @@ public class SpecialCardAi {
|
||||
// Sarkhan the Mad
|
||||
public static class SarkhanTheMad {
|
||||
public static boolean considerDig(final Player ai, final SpellAbility sa) {
|
||||
return sa.getHostCard().getCounters(CounterType.LOYALTY) == 1;
|
||||
return sa.getHostCard().getCounters(CounterEnumType.LOYALTY) == 1;
|
||||
}
|
||||
|
||||
public static boolean considerMakeDragon(final Player ai, final SpellAbility sa) {
|
||||
@@ -1109,7 +1109,7 @@ public class SpecialCardAi {
|
||||
// Sorin, Vengeful Bloodlord
|
||||
public static class SorinVengefulBloodlord {
|
||||
public static boolean consider(final Player ai, final SpellAbility sa) {
|
||||
int loyalty = sa.getHostCard().getCounters(CounterType.LOYALTY);
|
||||
int loyalty = sa.getHostCard().getCounters(CounterEnumType.LOYALTY);
|
||||
CardCollection creaturesToGet = CardLists.filter(ai.getCardsIn(ZoneType.Graveyard),
|
||||
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.lessCMC(loyalty - 1), new Predicate<Card>() {
|
||||
@Override
|
||||
@@ -1365,7 +1365,7 @@ public class SpecialCardAi {
|
||||
Card source = sa.getHostCard();
|
||||
Game game = source.getGame();
|
||||
|
||||
final int loyalty = source.getCounters(CounterType.LOYALTY);
|
||||
final int loyalty = source.getCounters(CounterEnumType.LOYALTY);
|
||||
int x = -1, best = 0;
|
||||
Card single = null;
|
||||
for (int i = 0; i < loyalty; i++) {
|
||||
|
||||
@@ -307,7 +307,7 @@ public abstract class SpellAbilityAi {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
|
||||
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
boolean hasPlayer = false;
|
||||
boolean hasCard = false;
|
||||
boolean hasPlaneswalker = false;
|
||||
@@ -324,11 +324,11 @@ public abstract class SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (hasPlayer && hasPlaneswalker) {
|
||||
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options);
|
||||
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options, params);
|
||||
} else if (hasCard) {
|
||||
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer);
|
||||
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer, params);
|
||||
} else if (hasPlayer) {
|
||||
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options);
|
||||
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options, params);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -339,17 +339,17 @@ public abstract class SpellAbilityAi {
|
||||
return spells.get(0);
|
||||
}
|
||||
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayer is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
|
||||
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
|
||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayerOrPlaneswalker is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
@@ -23,7 +25,7 @@ public class AmassAi extends SpellAbilityAi {
|
||||
final Game game = ai.getGame();
|
||||
|
||||
if (!aiArmies.isEmpty()) {
|
||||
return CardLists.count(aiArmies, CardPredicates.canReceiveCounters(CounterType.P1P1)) > 0;
|
||||
return CardLists.count(aiArmies, CardPredicates.canReceiveCounters(CounterEnumType.P1P1)) > 0;
|
||||
} else {
|
||||
final String tokenScript = "b_0_0_zombie_army";
|
||||
final int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Num", "1"), sa);
|
||||
@@ -44,8 +46,8 @@ public class AmassAi extends SpellAbilityAi {
|
||||
CardCollection preList = new CardCollection(token);
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(token), preList);
|
||||
|
||||
if (token.canReceiveCounters(CounterType.P1P1)) {
|
||||
token.setCounters(CounterType.P1P1, amount);
|
||||
if (token.canReceiveCounters(CounterEnumType.P1P1)) {
|
||||
token.setCounters(CounterEnumType.P1P1, amount);
|
||||
}
|
||||
|
||||
if (token.isCreature() && token.getNetToughness() < 1) {
|
||||
@@ -86,8 +88,8 @@ public class AmassAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
Iterable<Card> better = CardLists.filter(options, CardPredicates.canReceiveCounters(CounterType.P1P1));
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
Iterable<Card> better = CardLists.filter(options, CardPredicates.canReceiveCounters(CounterEnumType.P1P1));
|
||||
if (Iterables.isEmpty(better)) {
|
||||
better = options;
|
||||
}
|
||||
|
||||
@@ -1704,12 +1704,12 @@ public class AttachAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
return attachToCardAIPreferences(ai, sa, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
return attachToPlayerAIPreferences(ai, sa, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import forge.ai.ComputerUtilCard;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.card.Card;
|
||||
@@ -50,7 +52,7 @@ public final class BondAi extends SpellAbilityAi {
|
||||
|
||||
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
return ComputerUtilCard.getBestCreatureAI(options);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.game.player.PlayerPredicates;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChangeCombatantsAi extends SpellAbilityAi {
|
||||
/* (non-Javadoc)
|
||||
@@ -45,7 +46,7 @@ public class ChangeCombatantsAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
|
||||
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
PlayerCollection targetableOpps = new PlayerCollection();
|
||||
for (GameEntity p : options) {
|
||||
if (p instanceof Player && !p.equals(sa.getHostCard().getController())) {
|
||||
|
||||
@@ -142,7 +142,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
* </p>
|
||||
* @param sa
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
*
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
@Override
|
||||
@@ -170,7 +170,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
* @param mandatory
|
||||
* a boolean.
|
||||
*
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
@Override
|
||||
@@ -370,10 +370,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
if (!activateForCost && list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if ("Atarka's Command".equals(sourceName)
|
||||
&& (list.size() < 2 || ai.getLandsPlayedThisTurn() < 1)) {
|
||||
// be strict on playing lands off charms
|
||||
return false;
|
||||
if ("Atarka's Command".equals(sourceName)
|
||||
&& (list.size() < 2 || ai.getLandsPlayedThisTurn() < 1)) {
|
||||
// be strict on playing lands off charms
|
||||
return false;
|
||||
}
|
||||
|
||||
String num = sa.getParam("ChangeNum");
|
||||
@@ -385,7 +385,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
source.setSVar("PayX", Integer.toString(xPay));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (sourceName.equals("Temur Sabertooth")) {
|
||||
// activated bounce + pump
|
||||
if (ComputerUtilCard.shouldPumpCard(ai, sa.getSubAbility(), source, 0, 0, Arrays.asList("Indestructible")) ||
|
||||
@@ -400,9 +400,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (ComputerUtil.playImmediately(ai, sa)) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// don't use fetching to top of library/graveyard before main2
|
||||
@@ -418,9 +418,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (ComputerUtil.waitForBlocking(sa)) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final AbilitySub subAb = sa.getSubAbility();
|
||||
return subAb == null || SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(ai, subAb);
|
||||
}
|
||||
@@ -551,7 +551,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
* basicManaFixing.
|
||||
* </p>
|
||||
* @param ai
|
||||
*
|
||||
*
|
||||
* @param list
|
||||
* a List<Card> object.
|
||||
* @return a {@link forge.game.card.Card} object.
|
||||
@@ -584,7 +584,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
if (minType != null) {
|
||||
result = CardLists.getType(list, minType);
|
||||
}
|
||||
|
||||
|
||||
// pick dual lands if available
|
||||
if (Iterables.any(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS))) {
|
||||
result = CardLists.filter(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
|
||||
@@ -597,7 +597,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
* <p>
|
||||
* areAllBasics.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param types
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
@@ -617,8 +617,8 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
* @return Card
|
||||
*/
|
||||
private static Card chooseCreature(final Player ai, CardCollection list) {
|
||||
// Creating a new combat for testing purposes.
|
||||
final Player opponent = ai.getWeakestOpponent();
|
||||
// Creating a new combat for testing purposes.
|
||||
final Player opponent = ai.getWeakestOpponent();
|
||||
Combat combat = new Combat(opponent);
|
||||
for (Card att : opponent.getCreaturesInPlay()) {
|
||||
combat.addAttacker(att, ai);
|
||||
@@ -742,7 +742,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see
|
||||
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
|
||||
@@ -781,7 +781,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//don't unearth after attacking is possible
|
||||
if (sa.hasParam("Unearth") && ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
|
||||
return false;
|
||||
@@ -895,7 +895,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
immediately |= ComputerUtil.playImmediately(ai, sa);
|
||||
|
||||
// Narrow down the list:
|
||||
@@ -926,7 +926,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
CardCollection blockers = currCombat.getBlockers(attacker);
|
||||
// Save my attacker by bouncing a blocker
|
||||
if (attacker.getController().equals(ai) && attacker.getShieldCount() == 0
|
||||
&& ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)
|
||||
&& ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)
|
||||
&& !currCombat.getBlockers(attacker).isEmpty()) {
|
||||
ComputerUtilCard.sortByEvaluateCreature(blockers);
|
||||
Combat combat = new Combat(ai);
|
||||
@@ -970,9 +970,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
|
||||
sa.getTargets().add(tobounce);
|
||||
|
||||
boolean saheeliFelidarCombo = sa.getHostCard().getName().equals("Felidar Guardian")
|
||||
boolean saheeliFelidarCombo = sa.getHostCard().getName().equals("Felidar Guardian")
|
||||
&& tobounce.getName().equals("Saheeli Rai")
|
||||
&& CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Felidar Guardian")).size() <
|
||||
&& CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Felidar Guardian")).size() <
|
||||
CardLists.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.isType("Creature")).size() + ai.getOpponentsGreatestLifeTotal() + 10;
|
||||
|
||||
// remember that the card was bounced already unless it's a special combo case
|
||||
@@ -985,20 +985,20 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
// bounce opponent's stuff
|
||||
list = CardLists.filterControlledBy(list, ai.getOpponents());
|
||||
if (!CardLists.getNotType(list, "Land").isEmpty()) {
|
||||
// When bouncing opponents stuff other than lands, don't bounce cards with CMC 0
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
for (Card aura : c.getEnchantedBy()) {
|
||||
// When bouncing opponents stuff other than lands, don't bounce cards with CMC 0
|
||||
list = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
for (Card aura : c.getEnchantedBy()) {
|
||||
return aura.getController().isOpponentOf(ai);
|
||||
}
|
||||
if (blink) {
|
||||
return c.isToken();
|
||||
} else {
|
||||
return c.isToken() || c.getCMC() > 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (blink) {
|
||||
return c.isToken();
|
||||
} else {
|
||||
return c.isToken() || c.getCMC() > 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// TODO: Blink permanents with ETB triggers
|
||||
/*else if (!sa.isTrigger() && SpellAbilityAi.playReusable(ai, sa)) {
|
||||
@@ -1023,7 +1023,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
} else if (origin.contains(ZoneType.Graveyard)) {
|
||||
if (destination.equals(ZoneType.Exile) || destination.equals(ZoneType.Library)) {
|
||||
if (destination.equals(ZoneType.Exile) || destination.equals(ZoneType.Library)) {
|
||||
// Don't use these abilities before main 2 if possible
|
||||
if (!immediately && game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
|
||||
&& !sa.hasParam("ActivationPhases") && !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||
@@ -1035,7 +1035,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
&& !ComputerUtil.activateForCost(sa, ai)) {
|
||||
return false;
|
||||
}
|
||||
} else if (destination.equals(ZoneType.Hand)) {
|
||||
} else if (destination.equals(ZoneType.Hand)) {
|
||||
// only retrieve cards from computer graveyard
|
||||
list = CardLists.filterControlledBy(list, ai);
|
||||
} else if (sa.hasParam("AttachedTo")) {
|
||||
@@ -1096,10 +1096,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
|
||||
// Only care about combatants during combat
|
||||
if (game.getPhaseHandler().inCombat() && origin.contains(ZoneType.Battlefield)) {
|
||||
CardCollection newList = CardLists.getValidCards(list, "Card.attacking,Card.blocking", null, null);
|
||||
if (!newList.isEmpty() || !sa.isTrigger()) {
|
||||
list = newList;
|
||||
}
|
||||
CardCollection newList = CardLists.getValidCards(list, "Card.attacking,Card.blocking", null, null);
|
||||
if (!newList.isEmpty() || !sa.isTrigger()) {
|
||||
list = newList;
|
||||
}
|
||||
}
|
||||
|
||||
boolean doWithoutTarget = sa.hasParam("Planeswalker") && sa.usesTargeting()
|
||||
@@ -1251,7 +1251,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a permanent threatened by a stack ability or in combat can
|
||||
* be saved by bouncing.
|
||||
@@ -1324,11 +1324,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
Collections.sort(aiPlaneswalkers, new Comparator<Card>() {
|
||||
@Override
|
||||
public int compare(final Card a, final Card b) {
|
||||
return a.getCounters(CounterType.LOYALTY) - b.getCounters(CounterType.LOYALTY);
|
||||
return a.getCounters(CounterEnumType.LOYALTY) - b.getCounters(CounterEnumType.LOYALTY);
|
||||
}
|
||||
});
|
||||
for (Card pw : aiPlaneswalkers) {
|
||||
int curLoyalty = pw.getCounters(CounterType.LOYALTY);
|
||||
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
||||
int freshLoyalty = Integer.valueOf(pw.getCurrentState().getBaseLoyalty());
|
||||
if (freshLoyalty - curLoyalty >= loyaltyDiff && curLoyalty <= maxLoyaltyToConsider) {
|
||||
return pw;
|
||||
@@ -1506,10 +1506,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
if (type == null) {
|
||||
type = "Card";
|
||||
}
|
||||
|
||||
|
||||
Card c = null;
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
|
||||
|
||||
CardLists.shuffle(fetchList);
|
||||
// Save a card as a default, in case we can't find anything suitable.
|
||||
Card first = fetchList.get(0);
|
||||
@@ -1614,19 +1614,19 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
// AI was never asked
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
// Called when looking for creature to attach aura or equipment
|
||||
return ComputerUtilCard.getBestAI(options);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
// Currently only used by Curse of Misfortunes, so this branch should never get hit
|
||||
// But just in case it does, just select the first option
|
||||
return Iterables.getFirst(options, null);
|
||||
@@ -1801,7 +1801,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
|
||||
if (causeSa != null && (causeSub = causeSa.getSubAbility()) != null) {
|
||||
ApiType subApi = causeSub.getApi();
|
||||
|
||||
|
||||
if (subApi == ApiType.ChangeZone && "Exile".equals(causeSub.getParam("Origin"))
|
||||
&& "Battlefield".equals(causeSub.getParam("Destination"))) {
|
||||
// A blink effect implemented using ChangeZone API
|
||||
@@ -1817,7 +1817,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
} else return causeSa.getHostCard() == null || !causeSa.getHostCard().equals(sa.getReplacingObject(AbilityKey.Card))
|
||||
|| !causeSa.getActivatingPlayer().equals(aiPlayer);
|
||||
}
|
||||
|
||||
|
||||
// Normally we want the commander back in Command zone to recast him later
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.util.MyRandom;
|
||||
import forge.util.collect.FCollection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CharmAi extends SpellAbilityAi {
|
||||
@Override
|
||||
@@ -232,7 +233,7 @@ public class CharmAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> opponents) {
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> opponents, Map<String, Object> params) {
|
||||
return Aggregates.random(opponents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package forge.ai.ability;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
@@ -20,7 +21,7 @@ import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
@@ -99,7 +100,7 @@ public class ChooseCardAi extends SpellAbilityAi {
|
||||
});
|
||||
return !choices.isEmpty();
|
||||
} else if (aiLogic.equals("Ashiok")) {
|
||||
final int loyalty = host.getCounters(CounterType.LOYALTY) - 1;
|
||||
final int loyalty = host.getCounters(CounterEnumType.LOYALTY) - 1;
|
||||
for (int i = loyalty; i >= 0; i--) {
|
||||
host.setSVar("ChosenX", "Number$" + i);
|
||||
choices = ai.getGame().getCardsIn(choiceZone);
|
||||
@@ -140,12 +141,12 @@ public class ChooseCardAi extends SpellAbilityAi {
|
||||
}
|
||||
return checkApiLogic(ai, sa);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||
*/
|
||||
@Override
|
||||
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
final Card host = sa.getHostCard();
|
||||
final Player ctrl = host.getController();
|
||||
final String logic = sa.getParam("AILogic");
|
||||
@@ -191,7 +192,7 @@ public class ChooseCardAi extends SpellAbilityAi {
|
||||
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
|
||||
return false;
|
||||
}
|
||||
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
|
||||
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
|
||||
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -23,7 +24,6 @@ public class ChooseCardNameAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
Card source = sa.getHostCard();
|
||||
if (sa.hasParam("AILogic")) {
|
||||
// Don't tap creatures that may be able to block
|
||||
if (ComputerUtil.waitForBlocking(sa)) {
|
||||
@@ -60,7 +60,7 @@ public class ChooseCardNameAi extends SpellAbilityAi {
|
||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||
*/
|
||||
@Override
|
||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
|
||||
return ComputerUtilCard.getBestAI(options);
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public class ChooseCardNameAi extends SpellAbilityAi {
|
||||
|
||||
if (rules.getSplitType() == CardSplitType.Split) {
|
||||
Card copy = CardUtil.getLKICopy(card);
|
||||
// for calcing i need only one split side
|
||||
// for calcing i need only one split side
|
||||
if (isOther) {
|
||||
copy.getCurrentState().copyFrom(card.getState(CardStateName.RightSplit), true);
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,7 @@ import forge.game.spellability.SpellAbility;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChooseCompanionAi extends SpellAbilityAi {
|
||||
|
||||
@@ -15,7 +16,7 @@ public class ChooseCompanionAi extends SpellAbilityAi {
|
||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||
*/
|
||||
@Override
|
||||
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
List<Card> cards = Lists.newArrayList(options);
|
||||
if (cards.isEmpty()) {
|
||||
return null;
|
||||
|
||||
@@ -180,25 +180,25 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||
Card imprinted = host.getImprintedCards().getFirst();
|
||||
int dmg = imprinted.getCMC();
|
||||
Player owner = imprinted.getOwner();
|
||||
|
||||
|
||||
//useless cards in hand
|
||||
if (imprinted.getName().equals("Bridge from Below") ||
|
||||
imprinted.getName().equals("Haakon, Stromgald Scourge")) {
|
||||
return allow;
|
||||
}
|
||||
|
||||
|
||||
//bad cards when are thrown from the library to the graveyard, but Yixlid can prevent that
|
||||
if (!player.getGame().isCardInPlay("Yixlid Jailer") && (
|
||||
imprinted.getName().equals("Gaea's Blessing") ||
|
||||
imprinted.getName().equals("Narcomoeba"))) {
|
||||
return allow;
|
||||
}
|
||||
|
||||
|
||||
// milling against Tamiyo is pointless
|
||||
if (owner.isCardInCommand("Emblem - Tamiyo, the Moon Sage")) {
|
||||
return allow;
|
||||
}
|
||||
|
||||
|
||||
// milling a land against Gitrog result in card draw
|
||||
if (imprinted.isLand() && owner.isCardInPlay("The Gitrog Monster")) {
|
||||
// try to mill owner
|
||||
@@ -207,19 +207,19 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||
}
|
||||
return allow;
|
||||
}
|
||||
|
||||
|
||||
// milling a creature against Sidisi result in more creatures
|
||||
if (imprinted.isCreature() && owner.isCardInPlay("Sidisi, Brood Tyrant")) {
|
||||
return allow;
|
||||
}
|
||||
|
||||
//if Iona does prevent from casting, allow it to draw
|
||||
//if Iona does prevent from casting, allow it to draw
|
||||
for (final Card io : player.getCardsIn(ZoneType.Battlefield, "Iona, Shield of Emeria")) {
|
||||
if (CardUtil.getColors(imprinted).hasAnyColor(MagicColor.fromName(io.getChosenColor()))) {
|
||||
return allow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dmg == 0) {
|
||||
// If CMC = 0, mill it!
|
||||
return deny;
|
||||
@@ -244,7 +244,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||
SpellAbility counterSA = spells.get(0), tokenSA = spells.get(1);
|
||||
|
||||
// check for something which might prevent the counters to be placed on host
|
||||
if (!host.canReceiveCounters(CounterType.P1P1)) {
|
||||
if (!host.canReceiveCounters(CounterEnumType.P1P1)) {
|
||||
return tokenSA;
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||
// need a copy for one with extra +1/+1 counter boost,
|
||||
// without causing triggers to run
|
||||
final Card copy = CardUtil.getLKICopy(host);
|
||||
copy.setCounters(CounterType.P1P1, copy.getCounters(CounterType.P1P1) + n);
|
||||
copy.setCounters(CounterEnumType.P1P1, copy.getCounters(CounterEnumType.P1P1) + n);
|
||||
copy.setZone(host.getZone());
|
||||
|
||||
// if host would put into the battlefield attacking
|
||||
@@ -283,7 +283,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||
// in this cases Token might be prefered even if they would not survive
|
||||
final Card tokenCard = TokenAi.spawnToken(player, tokenSA);
|
||||
|
||||
// Token would not survive
|
||||
// Token would not survive
|
||||
if (!tokenCard.isCreature() || tokenCard.getNetToughness() < 1) {
|
||||
return counterSA;
|
||||
}
|
||||
@@ -336,7 +336,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||
filtered.add(sp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO find better way to check
|
||||
if (!filtered.isEmpty()) {
|
||||
return filtered.get(0);
|
||||
@@ -362,7 +362,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
||||
game.getAction().checkStaticAbilities(false);
|
||||
|
||||
// can't gain counters, use Haste
|
||||
if (!copy.canReceiveCounters(CounterType.P1P1)) {
|
||||
if (!copy.canReceiveCounters(CounterEnumType.P1P1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChoosePlayerAi extends SpellAbilityAi {
|
||||
@Override
|
||||
@@ -27,7 +28,7 @@ public class ChoosePlayerAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> choices) {
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> choices, Map<String, Object> params) {
|
||||
Player chosen = null;
|
||||
if ("Curse".equals(sa.getParam("AILogic"))) {
|
||||
for (Player pc : choices) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
@@ -126,7 +127,7 @@ public class ChooseSourceAi extends SpellAbilityAi {
|
||||
|
||||
|
||||
@Override
|
||||
public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
if ("NeedsPrevention".equals(sa.getParam("AILogic"))) {
|
||||
final Player ai = sa.getActivatingPlayer();
|
||||
final Game game = ai.getGame();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.ai.ComputerUtilCard;
|
||||
|
||||
@@ -56,7 +58,7 @@ public class ClashAi extends SpellAbilityAi {
|
||||
* forge.game.spellability.SpellAbility, java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
for (Player p : options) {
|
||||
if (p.getCardsIn(ZoneType.Library).size() == 0)
|
||||
return p;
|
||||
@@ -82,7 +84,7 @@ public class ClashAi extends SpellAbilityAi {
|
||||
|
||||
PlayerCollection players = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
||||
// use chooseSinglePlayer function to the select player
|
||||
Player chosen = chooseSinglePlayer(ai, sa, players);
|
||||
Player chosen = chooseSinglePlayer(ai, sa, players, null);
|
||||
if (chosen != null) {
|
||||
sa.resetTargets();
|
||||
sa.getTargets().add(chosen);
|
||||
|
||||
@@ -15,6 +15,7 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CloneAi extends SpellAbilityAi {
|
||||
|
||||
@@ -169,7 +170,7 @@ public class CloneAi extends SpellAbilityAi {
|
||||
*/
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
||||
Player targetedPlayer) {
|
||||
Player targetedPlayer, Map<String, Object> params) {
|
||||
|
||||
final Card host = sa.getHostCard();
|
||||
final Player ctrl = host.getController();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -302,7 +303,7 @@ public class ControlGainAi extends SpellAbilityAi {
|
||||
} // pumpDrawbackAI()
|
||||
|
||||
@Override
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
final List<Card> cards = Lists.newArrayList();
|
||||
for (Player p : options) {
|
||||
cards.addAll(p.getCreaturesInPlay());
|
||||
|
||||
@@ -19,6 +19,7 @@ import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CopyPermanentAi extends SpellAbilityAi {
|
||||
@Override
|
||||
@@ -204,7 +205,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||
*/
|
||||
@Override
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
// Select a card to attach to
|
||||
CardCollection betterOptions = getBetterOptions(ai, sa, options, isOptional);
|
||||
if (!betterOptions.isEmpty()) {
|
||||
@@ -223,7 +224,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
final List<Card> cards = new PlayerCollection(options).getCreaturesInPlay();
|
||||
Card chosen = ComputerUtilCard.getBestCreatureAI(cards);
|
||||
return chosen != null ? chosen.getController() : Iterables.getFirst(options, null);
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -26,7 +26,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
@@ -35,7 +35,7 @@ import forge.util.Aggregates;
|
||||
* <p>
|
||||
* AbilityFactory_Counters class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -46,7 +46,7 @@ public abstract class CountersAi {
|
||||
* <p>
|
||||
* chooseCursedTarget.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param list
|
||||
* a {@link forge.CardList} object.
|
||||
* @param type
|
||||
@@ -77,7 +77,7 @@ public abstract class CountersAi {
|
||||
* <p>
|
||||
* chooseBoonTarget.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param list
|
||||
* a {@link forge.CardList} object.
|
||||
* @param type
|
||||
@@ -97,7 +97,7 @@ public abstract class CountersAi {
|
||||
final CardCollection boon = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
return c.getCounters(CounterType.DIVINITY) == 0;
|
||||
return c.getCounters(CounterEnumType.DIVINITY) == 0;
|
||||
}
|
||||
});
|
||||
choice = ComputerUtilCard.getMostExpensivePermanentAI(boon, null, false);
|
||||
|
||||
@@ -42,14 +42,14 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||
final Card host = sa.getHostCard();
|
||||
final String type = sa.getParam("CounterType");
|
||||
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
|
||||
final CounterType cType = "Any".equals(type) ? null : CounterType.getType(type);
|
||||
|
||||
// Don't tap creatures that may be able to block
|
||||
if (ComputerUtil.waitForBlocking(sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CounterType.P1P1.equals(cType) && sa.hasParam("Source")) {
|
||||
if (CounterEnumType.P1P1.equals(cType) && sa.hasParam("Source")) {
|
||||
int amount = calcAmount(sa, cType);
|
||||
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
||||
if (ph.getPlayerTurn().isOpponentOf(ai)) {
|
||||
@@ -92,7 +92,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
// for Simic Fluxmage and other
|
||||
return ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN);
|
||||
|
||||
} else if (CounterType.P1P1.equals(cType) && sa.hasParam("Defined")) {
|
||||
} else if (CounterEnumType.P1P1.equals(cType) && sa.hasParam("Defined")) {
|
||||
// something like Cyptoplast Root-kin
|
||||
if (ph.getPlayerTurn().isOpponentOf(ai)) {
|
||||
if (ph.inCombat() && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
@@ -115,6 +115,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
|
||||
|
||||
if (sa.usesTargeting()) {
|
||||
sa.resetTargets();
|
||||
|
||||
if (!moveTgtAI(ai, sa) && !mandatory) {
|
||||
return false;
|
||||
@@ -142,7 +143,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
final Card host = sa.getHostCard();
|
||||
|
||||
final String type = sa.getParam("CounterType");
|
||||
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
|
||||
final CounterType cType = "Any".equals(type) ? null : CounterType.getType(type);
|
||||
|
||||
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
|
||||
final List<Card> destCards = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
||||
@@ -189,7 +190,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
|
||||
// check for some specific AI preferences
|
||||
if ("DontMoveCounterIfLethal".equals(sa.getParam("AILogic"))) {
|
||||
return cType != CounterType.P1P1 || src.getNetToughness() - src.getTempToughnessBoost() - 1 > 0;
|
||||
return !cType.is(CounterEnumType.P1P1) || src.getNetToughness() - src.getTempToughnessBoost() - 1 > 0;
|
||||
}
|
||||
}
|
||||
// no target
|
||||
@@ -234,7 +235,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
final Card host = sa.getHostCard();
|
||||
final Game game = ai.getGame();
|
||||
final String type = sa.getParam("CounterType");
|
||||
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
|
||||
final CounterType cType = "Any".equals(type) || "All".equals(type) ? null : CounterType.getType(type);
|
||||
|
||||
List<Card> tgtCards = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
|
||||
|
||||
@@ -278,7 +279,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
|
||||
// do not steal a P1P1 from Undying if it would die
|
||||
// this way
|
||||
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||
if (CounterEnumType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken();
|
||||
}
|
||||
return true;
|
||||
@@ -321,13 +322,13 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// try to remove P1P1 from undying or evolve
|
||||
if (CounterType.P1P1.equals(cType)) {
|
||||
if (CounterEnumType.P1P1.equals(cType)) {
|
||||
if (card.hasKeyword(Keyword.UNDYING) || card.hasKeyword(Keyword.EVOLVE)
|
||||
|| card.hasKeyword(Keyword.ADAPT)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (CounterType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
|
||||
if (CounterEnumType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -382,10 +383,10 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (cType != null) {
|
||||
if (CounterType.P1P1.equals(cType) && card.hasKeyword(Keyword.UNDYING)) {
|
||||
if (CounterEnumType.P1P1.equals(cType) && card.hasKeyword(Keyword.UNDYING)) {
|
||||
return false;
|
||||
}
|
||||
if (CounterType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
|
||||
if (CounterEnumType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -393,7 +394,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -452,7 +453,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
||||
// or for source -> multiple defined
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
||||
Player targetedPlayer) {
|
||||
Player targetedPlayer, Map<String, Object> params) {
|
||||
if (sa.hasParam("AiLogic")) {
|
||||
String logic = sa.getParam("AiLogic");
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
@@ -77,7 +78,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
|
||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||
final CounterType counterType = getCounterType(sa);
|
||||
|
||||
if (!CounterType.P1P1.equals(counterType) && counterType != null) {
|
||||
if (!CounterEnumType.P1P1.equals(counterType) && counterType != null) {
|
||||
if (!sa.hasParam("ActivationPhases")) {
|
||||
// Don't use non P1P1/M1M1 counters before main 2 if possible
|
||||
if (ph.getPhase().isBefore(PhaseType.MAIN2) && !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||
@@ -147,15 +148,15 @@ public class CountersMultiplyAi extends SpellAbilityAi {
|
||||
if (!aiList.isEmpty()) {
|
||||
// counter type list to check
|
||||
// first loyalty, then P1P!, then Charge Counter
|
||||
List<CounterType> typeList = Lists.newArrayList(CounterType.LOYALTY, CounterType.P1P1, CounterType.CHARGE);
|
||||
for (CounterType type : typeList) {
|
||||
List<CounterEnumType> typeList = Lists.newArrayList(CounterEnumType.LOYALTY, CounterEnumType.P1P1, CounterEnumType.CHARGE);
|
||||
for (CounterEnumType type : typeList) {
|
||||
// enough targets
|
||||
if (!sa.canAddMoreTarget()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (counterType == null || counterType == type) {
|
||||
addTargetsByCounterType(ai, sa, aiList, type);
|
||||
if (counterType == null || counterType.is(type)) {
|
||||
addTargetsByCounterType(ai, sa, aiList, CounterType.get(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +165,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
|
||||
if (!oppList.isEmpty()) {
|
||||
// not enough targets
|
||||
if (sa.canAddMoreTarget()) {
|
||||
final CounterType type = CounterType.M1M1;
|
||||
final CounterType type = CounterType.get(CounterEnumType.M1M1);
|
||||
if (counterType == null || counterType == type) {
|
||||
addTargetsByCounterType(ai, sa, oppList, type);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import forge.game.GameEntity;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -32,7 +33,7 @@ public class CountersProliferateAi extends SpellAbilityAi {
|
||||
|
||||
for (final Player p : allies) {
|
||||
// player has experience or energy counter
|
||||
if (p.getCounters(CounterType.EXPERIENCE) + p.getCounters(CounterType.ENERGY) >= 1) {
|
||||
if (p.getCounters(CounterEnumType.EXPERIENCE) + p.getCounters(CounterEnumType.ENERGY) >= 1) {
|
||||
allyExpOrEnergy = true;
|
||||
}
|
||||
cperms.addAll(CardLists.filter(p.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
|
||||
@@ -115,17 +116,19 @@ public class CountersProliferateAi extends SpellAbilityAi {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
|
||||
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
// Proliferate is always optional for all, no need to select best
|
||||
|
||||
final CounterType poison = CounterType.get(CounterEnumType.POISON);
|
||||
|
||||
// because countertype can't be chosen anymore, only look for posion counters
|
||||
for (final Player p : Iterables.filter(options, Player.class)) {
|
||||
if (p.isOpponentOf(ai)) {
|
||||
if (p.getCounters(CounterType.POISON) > 0 && p.canReceiveCounters(CounterType.POISON)) {
|
||||
if (p.getCounters(poison) > 0 && p.canReceiveCounters(poison)) {
|
||||
return (T)p;
|
||||
}
|
||||
} else {
|
||||
if (p.getCounters(CounterType.POISON) <= 5 || p.canReceiveCounters(CounterType.POISON)) {
|
||||
if (p.getCounters(poison) <= 5 || p.canReceiveCounters(poison)) {
|
||||
return (T)p;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#willPayCosts(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility, forge.game.cost.Cost,
|
||||
* forge.game.card.Card)
|
||||
@@ -56,17 +56,17 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
if (part instanceof CostRemoveCounter) {
|
||||
final CostRemoveCounter remCounter = (CostRemoveCounter) part;
|
||||
final CounterType counterType = remCounter.counter;
|
||||
if (counterType.name().equals(type) && !aiLogic.startsWith("MoveCounter")) {
|
||||
if (counterType.getName().equals(type) && !aiLogic.startsWith("MoveCounter")) {
|
||||
return false;
|
||||
}
|
||||
if (!part.payCostFromSource()) {
|
||||
if (counterType.equals(CounterType.P1P1)) {
|
||||
if (counterType.is(CounterEnumType.P1P1)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// don't kill the creature
|
||||
if (counterType.equals(CounterType.P1P1) && source.getLethalDamage() <= 1) {
|
||||
if (counterType.is(CounterEnumType.P1P1) && source.getLethalDamage() <= 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see
|
||||
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
|
||||
@@ -109,7 +109,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
int maxLevel = Integer.parseInt(sa.getParam("MaxLevel"));
|
||||
return source.getCounters(CounterType.LEVEL) < maxLevel;
|
||||
return source.getCounters(CounterEnumType.LEVEL) < maxLevel;
|
||||
}
|
||||
|
||||
return super.checkPhaseRestrictions(ai, sa, ph);
|
||||
@@ -146,7 +146,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
if (abTgt.canTgtPlayer()) {
|
||||
// try to kill opponent with Poison
|
||||
PlayerCollection oppList = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
||||
PlayerCollection poisonList = oppList.filter(PlayerPredicates.hasCounter(CounterType.POISON, 9));
|
||||
PlayerCollection poisonList = oppList.filter(PlayerPredicates.hasCounter(CounterEnumType.POISON, 9));
|
||||
if (!poisonList.isEmpty()) {
|
||||
sa.getTargets().add(poisonList.max(PlayerPredicates.compareByLife()));
|
||||
return true;
|
||||
@@ -157,13 +157,13 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
// try to kill creature with -1/-1 counters if it can
|
||||
// receive counters, execpt it has undying
|
||||
CardCollection oppCreat = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
|
||||
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterType.M1M1));
|
||||
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterEnumType.M1M1));
|
||||
oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, Keyword.UNDYING);
|
||||
|
||||
oppCreatM1 = CardLists.filter(oppCreatM1, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card input) {
|
||||
return input.getNetToughness() <= 1 && input.canReceiveCounters(CounterType.M1M1);
|
||||
return input.getNetToughness() <= 1 && input.canReceiveCounters(CounterType.get(CounterEnumType.M1M1));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -244,7 +244,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
int totBlkPower = Aggregates.sum(blocked, CardPredicates.Accessors.fnGetNetPower);
|
||||
int totBlkToughness = Aggregates.min(blocked, CardPredicates.Accessors.fnGetNetToughness);
|
||||
|
||||
int numActivations = ai.getCounters(CounterType.ENERGY) / sa.getPayCosts().getCostEnergy().convertAmount();
|
||||
int numActivations = ai.getCounters(CounterEnumType.ENERGY) / sa.getPayCosts().getCostEnergy().convertAmount();
|
||||
if (sa.getHostCard().getNetToughness() + numActivations > totBlkPower
|
||||
|| sa.getHostCard().getNetPower() + numActivations >= totBlkToughness) {
|
||||
return true;
|
||||
@@ -259,7 +259,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
AiCardMemory.rememberCard(ai, source, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN);
|
||||
return true;
|
||||
}
|
||||
} else if (ai.getCounters(CounterType.ENERGY) > ComputerUtilCard.getMaxSAEnergyCostOnBattlefield(ai) + sa.getPayCosts().getCostEnergy().convertAmount()) {
|
||||
} else if (ai.getCounters(CounterEnumType.ENERGY) > ComputerUtilCard.getMaxSAEnergyCostOnBattlefield(ai) + sa.getPayCosts().getCostEnergy().convertAmount()) {
|
||||
// outside of combat, this logic only works if the relevant AI profile option is enabled
|
||||
// and if there is enough energy saved
|
||||
if (!onlyInCombat) {
|
||||
@@ -293,7 +293,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (sourceName.equals("Feat of Resistance")) { // sub-ability should take precedence
|
||||
CardCollection prot = ProtectAi.getProtectCreatures(ai, sa.getSubAbility());
|
||||
if (!prot.isEmpty()) {
|
||||
@@ -322,7 +322,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
Game game = ai.getGame();
|
||||
Combat combat = game.getCombat();
|
||||
|
||||
if (!source.canReceiveCounters(CounterType.P1P1) || source.getCounters(CounterType.P1P1) > 0) {
|
||||
if (!source.canReceiveCounters(CounterType.get(CounterEnumType.P1P1)) || source.getCounters(CounterEnumType.P1P1) > 0) {
|
||||
return false;
|
||||
} else if (combat != null && ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
return doCombatAdaptLogic(source, amount, combat);
|
||||
@@ -336,7 +336,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
}
|
||||
return FightAi.canFightAi(ai, sa, nPump, nPump);
|
||||
}
|
||||
|
||||
|
||||
if (amountStr.equals("X")) {
|
||||
if (source.getSVar(amountStr).equals("Count$xPaid")) {
|
||||
// By default, set PayX here to maximum value (used for most SAs of this type).
|
||||
@@ -345,7 +345,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
if (isClockwork) {
|
||||
// Clockwork Avian and other similar cards: do not tap all mana for X,
|
||||
// instead only rewind to max allowed value when the power gets low enough.
|
||||
int curCtrs = source.getCounters(CounterType.P1P0);
|
||||
int curCtrs = source.getCounters(CounterEnumType.P1P0);
|
||||
int maxCtrs = Integer.parseInt(sa.getParam("MaxFromEffect"));
|
||||
|
||||
// This will "rewind" clockwork cards when they fall to 50% power or below, consider improving
|
||||
@@ -396,7 +396,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
||||
// only evaluates case where all tokens are placed on a single target
|
||||
@@ -417,8 +417,8 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
|
||||
// Targeting
|
||||
if (sa.usesTargeting()) {
|
||||
sa.resetTargets();
|
||||
|
||||
sa.resetTargets();
|
||||
|
||||
final boolean sacSelf = ComputerUtilCost.isSacrificeSelfCost(abCost);
|
||||
|
||||
if (sa.isCurse()) {
|
||||
@@ -435,7 +435,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
if (sacSelf && c.equals(source)) {
|
||||
return false;
|
||||
}
|
||||
return sa.canTarget(c) && c.canReceiveCounters(CounterType.valueOf(type));
|
||||
return sa.canTarget(c) && c.canReceiveCounters(CounterType.getType(type));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -488,7 +488,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// target loop
|
||||
while (sa.canAddMoreTarget()) {
|
||||
if (list.isEmpty()) {
|
||||
@@ -558,7 +558,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int currCounters = cards.get(0).getCounters(CounterType.valueOf(type));
|
||||
final int currCounters = cards.get(0).getCounters(CounterType.get(type));
|
||||
// each non +1/+1 counter on the card is a 10% chance of not
|
||||
// activating this ability.
|
||||
|
||||
@@ -574,11 +574,11 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
boolean immediately = ComputerUtil.playImmediately(ai, sa);
|
||||
|
||||
|
||||
if (abCost != null && !ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa, immediately)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (immediately) {
|
||||
return true;
|
||||
}
|
||||
@@ -612,7 +612,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||
|
||||
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
|
||||
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
|
||||
|| (sa.getRootAbility().isTrigger() && !sa.getRootAbility().isOptionalTrigger());
|
||||
|
||||
if (sa.usesTargeting()) {
|
||||
@@ -692,12 +692,12 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||
int left = amount;
|
||||
|
||||
|
||||
if (!sa.usesTargeting()) {
|
||||
// No target. So must be defined
|
||||
list = new CardCollection(AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa));
|
||||
|
||||
if (amountStr.equals("X")
|
||||
|
||||
if (amountStr.equals("X")
|
||||
&& !source.hasSVar("PayX") /* SubAbility on something that already had set PayX, e.g. Endless One ETB counters */
|
||||
&& ((sa.hasParam(amountStr) && sa.getSVar(amountStr).equals("Count$xPaid")) || source.getSVar(amountStr).equals("Count$xPaid") )) {
|
||||
|
||||
@@ -727,7 +727,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
|
||||
source.setSVar("PayX", Integer.toString(payX));
|
||||
}
|
||||
|
||||
|
||||
if (!mandatory) {
|
||||
// TODO - If Trigger isn't mandatory, when wouldn't we want to
|
||||
// put a counter?
|
||||
@@ -854,7 +854,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
List<Card> threatening = CardLists.filter(creats, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card c) {
|
||||
return CombatUtil.canBlock(source, c, !isHaste)
|
||||
return CombatUtil.canBlock(source, c, !isHaste)
|
||||
&& (c.getNetToughness() > source.getNetPower() + tributeAmount || c.hasKeyword(Keyword.DEATHTOUCH));
|
||||
}
|
||||
});
|
||||
@@ -889,24 +889,28 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
// used by Tribute, select player with lowest Life
|
||||
// TODO add more logic using TributeAILogic
|
||||
List<Player> list = Lists.newArrayList(options);
|
||||
return Collections.min(list, PlayerPredicates.compareByLife());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
protected Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
// Bolster does use this
|
||||
// TODO need more or less logic there?
|
||||
final CounterType m1m1 = CounterType.get(CounterEnumType.M1M1);
|
||||
final CounterType p1p1 = CounterType.get(CounterEnumType.P1P1);
|
||||
|
||||
// no logic if there is no options or no to choice
|
||||
if (!isOptional && Iterables.size(options) <= 1) {
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
|
||||
final CounterType type = params.containsKey("CounterType") ? (CounterType)params.get("CounterType")
|
||||
: CounterType.getType(sa.getParam("CounterType"));
|
||||
|
||||
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||
|
||||
@@ -923,7 +927,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
return false;
|
||||
if (ComputerUtilCard.isUselessCreature(ai, input))
|
||||
return false;
|
||||
if (CounterType.M1M1.equals(type) && amount >= input.getNetToughness())
|
||||
if (type.is(CounterEnumType.M1M1) && amount >= input.getNetToughness())
|
||||
return true;
|
||||
return ComputerUtil.isNegativeCounter(type, input);
|
||||
}
|
||||
@@ -947,6 +951,20 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
|
||||
CardCollection filtered = mine;
|
||||
|
||||
// Try to filter out keywords that we already have on cards
|
||||
if (type.isKeywordCounter()) {
|
||||
Keyword kw = Keyword.smartValueOf(type.getName());
|
||||
final CardCollection doNotHaveKeyword = CardLists.filter(filtered, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card card) {
|
||||
return !card.hasKeyword(kw) && card.canBeTargetedBy(sa) && sa.canTarget(card);
|
||||
}
|
||||
});
|
||||
|
||||
if (doNotHaveKeyword.size() > 0)
|
||||
filtered = doNotHaveKeyword;
|
||||
}
|
||||
|
||||
final CardCollection notUseless = CardLists.filter(filtered, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card input) {
|
||||
@@ -961,26 +979,26 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// some special logic to reload Persist/Undying
|
||||
if (CounterType.P1P1.equals(type)) {
|
||||
if (p1p1.equals(type)) {
|
||||
final CardCollection persist = CardLists.filter(filtered, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card input) {
|
||||
if (!input.hasKeyword(Keyword.PERSIST))
|
||||
return false;
|
||||
return input.getCounters(CounterType.M1M1) <= amount;
|
||||
return input.getCounters(m1m1) <= amount;
|
||||
}
|
||||
});
|
||||
|
||||
if (!persist.isEmpty()) {
|
||||
filtered = persist;
|
||||
}
|
||||
} else if (CounterType.M1M1.equals(type)) {
|
||||
} else if (m1m1.equals(type)) {
|
||||
final CardCollection undying = CardLists.filter(filtered, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card input) {
|
||||
if (!input.hasKeyword(Keyword.UNDYING))
|
||||
return false;
|
||||
return input.getCounters(CounterType.P1P1) <= amount && input.getNetToughness() > amount;
|
||||
return input.getCounters(p1p1) <= amount && input.getNetToughness() > amount;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1003,8 +1021,8 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
if (e instanceof Card) {
|
||||
Card c = (Card) e;
|
||||
if (c.getController().isOpponentOf(ai)) {
|
||||
if (options.contains(CounterType.M1M1) && !c.hasKeyword(Keyword.UNDYING)) {
|
||||
return CounterType.M1M1;
|
||||
if (options.contains(CounterType.get(CounterEnumType.M1M1)) && !c.hasKeyword(Keyword.UNDYING)) {
|
||||
return CounterType.get(CounterEnumType.M1M1);
|
||||
}
|
||||
for (CounterType type : options) {
|
||||
if (ComputerUtil.isNegativeCounter(type, c)) {
|
||||
@@ -1021,12 +1039,12 @@ public class CountersPutAi extends SpellAbilityAi {
|
||||
} else if (e instanceof Player) {
|
||||
Player p = (Player) e;
|
||||
if (p.isOpponentOf(ai)) {
|
||||
if (options.contains(CounterType.POISON)) {
|
||||
return CounterType.POISON;
|
||||
if (options.contains(CounterType.get(CounterEnumType.POISON))) {
|
||||
return CounterType.get(CounterEnumType.POISON);
|
||||
}
|
||||
} else {
|
||||
if (options.contains(CounterType.EXPERIENCE)) {
|
||||
return CounterType.EXPERIENCE;
|
||||
if (options.contains(CounterType.get(CounterEnumType.EXPERIENCE))) {
|
||||
return CounterType.get(CounterEnumType.EXPERIENCE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -36,7 +36,7 @@ import java.util.Map;
|
||||
* <p>
|
||||
* AbilityFactory_PutOrRemoveCountersAi class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -44,7 +44,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility)
|
||||
*/
|
||||
@@ -75,7 +75,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
|
||||
if (sa.hasParam("CounterType")) {
|
||||
// currently only Jhoira's Timebug
|
||||
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
|
||||
final CounterType type = CounterType.getType(sa.getParam("CounterType"));
|
||||
|
||||
CardCollection countersList = CardLists.filter(list, CardPredicates.hasCounter(type, amount));
|
||||
|
||||
@@ -100,7 +100,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
|
||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||
CardCollectionView depthsList = CardLists.filter(countersList,
|
||||
CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterType.ICE));
|
||||
CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterEnumType.ICE));
|
||||
|
||||
if (!depthsList.isEmpty()) {
|
||||
sa.getTargets().add(depthsList.getFirst());
|
||||
@@ -113,7 +113,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
CardCollection planeswalkerList = CardLists.filter(
|
||||
CardLists.filterControlledBy(countersList, ai.getOpponents()),
|
||||
CardPredicates.Presets.PLANESWALKERS,
|
||||
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
|
||||
CardPredicates.hasLessCounter(CounterEnumType.LOYALTY, amount));
|
||||
|
||||
if (!planeswalkerList.isEmpty()) {
|
||||
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
|
||||
@@ -123,7 +123,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
// do as M1M1 part
|
||||
CardCollection aiList = CardLists.filterControlledBy(countersList, ai);
|
||||
|
||||
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
|
||||
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.M1M1));
|
||||
|
||||
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, Keyword.PERSIST);
|
||||
if (!aiPersistList.isEmpty()) {
|
||||
@@ -136,7 +136,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// do as P1P1 part
|
||||
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.P1P1));
|
||||
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.P1P1));
|
||||
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, Keyword.UNDYING);
|
||||
|
||||
if (!aiUndyingList.isEmpty()) {
|
||||
@@ -183,7 +183,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
|
||||
* forge.game.spellability.SpellAbility, java.util.Map)
|
||||
*/
|
||||
@@ -199,18 +199,18 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
Card tgt = (Card) params.get("Target");
|
||||
|
||||
// planeswalker has high priority for loyalty counters
|
||||
if (tgt.isPlaneswalker() && options.contains(CounterType.LOYALTY)) {
|
||||
return CounterType.LOYALTY;
|
||||
if (tgt.isPlaneswalker() && options.contains(CounterType.get(CounterEnumType.LOYALTY))) {
|
||||
return CounterType.get(CounterEnumType.LOYALTY);
|
||||
}
|
||||
|
||||
if (tgt.getController().isOpponentOf(ai)) {
|
||||
// creatures with BaseToughness below or equal zero might be
|
||||
// killed if their counters are removed
|
||||
if (tgt.isCreature() && tgt.getBaseToughness() <= 0) {
|
||||
if (options.contains(CounterType.P1P1)) {
|
||||
return CounterType.P1P1;
|
||||
} else if (options.contains(CounterType.M1M1)) {
|
||||
return CounterType.M1M1;
|
||||
if (options.contains(CounterType.get(CounterEnumType.P1P1))) {
|
||||
return CounterType.get(CounterEnumType.P1P1);
|
||||
} else if (options.contains(CounterType.get(CounterEnumType.M1M1))) {
|
||||
return CounterType.get(CounterEnumType.M1M1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,14 +222,14 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
}
|
||||
} else {
|
||||
// this counters are treat first to be removed
|
||||
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.ICE)) {
|
||||
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.get(CounterEnumType.ICE))) {
|
||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||
return CounterType.ICE;
|
||||
return CounterType.get(CounterEnumType.ICE);
|
||||
}
|
||||
} else if (tgt.hasKeyword(Keyword.UNDYING) && options.contains(CounterType.P1P1)) {
|
||||
return CounterType.P1P1;
|
||||
} else if (tgt.hasKeyword(Keyword.PERSIST) && options.contains(CounterType.M1M1)) {
|
||||
return CounterType.M1M1;
|
||||
} else if (tgt.hasKeyword(Keyword.UNDYING) && options.contains(CounterType.get(CounterEnumType.P1P1))) {
|
||||
return CounterType.get(CounterEnumType.P1P1);
|
||||
} else if (tgt.hasKeyword(Keyword.PERSIST) && options.contains(CounterType.get(CounterEnumType.M1M1))) {
|
||||
return CounterType.get(CounterEnumType.M1M1);
|
||||
}
|
||||
|
||||
// fallback logic, select positive counter to add more
|
||||
@@ -246,7 +246,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see
|
||||
* forge.ai.SpellAbilityAi#chooseBinary(forge.game.player.PlayerController.
|
||||
* BinaryChoiceType, forge.game.spellability.SpellAbility, java.util.Map)
|
||||
@@ -262,19 +262,19 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
||||
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
|
||||
|
||||
if (tgt.getController().isOpponentOf(ai)) {
|
||||
if (type.equals(CounterType.LOYALTY) && tgt.isPlaneswalker()) {
|
||||
if (type.is(CounterEnumType.LOYALTY) && tgt.isPlaneswalker()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ComputerUtil.isNegativeCounter(type, tgt);
|
||||
} else {
|
||||
if (type.equals(CounterType.ICE) && "Dark Depths".equals(tgt.getName())) {
|
||||
if (type.is(CounterEnumType.ICE) && "Dark Depths".equals(tgt.getName())) {
|
||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||
return false;
|
||||
}
|
||||
} else if (type.equals(CounterType.M1M1) && tgt.hasKeyword(Keyword.PERSIST)) {
|
||||
} else if (type.is(CounterEnumType.M1M1) && tgt.hasKeyword(Keyword.PERSIST)) {
|
||||
return false;
|
||||
} else if (type.equals(CounterType.P1P1) && tgt.hasKeyword(Keyword.UNDYING)) {
|
||||
} else if (type.is(CounterEnumType.P1P1) && tgt.hasKeyword(Keyword.UNDYING)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see
|
||||
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
|
||||
@@ -50,7 +50,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see
|
||||
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler,
|
||||
@@ -68,7 +68,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility)
|
||||
*/
|
||||
@@ -82,7 +82,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (!type.matches("Any") && !type.matches("All")) {
|
||||
final int currCounters = sa.getHostCard().getCounters(CounterType.valueOf(type));
|
||||
final int currCounters = sa.getHostCard().getCounters(CounterType.getType(type));
|
||||
if (currCounters < 1) {
|
||||
return false;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
|
||||
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
|
||||
CardPredicates.hasCounter(CounterType.ICE, 3));
|
||||
CardPredicates.hasCounter(CounterEnumType.ICE, 3));
|
||||
|
||||
if (!depthsList.isEmpty()) {
|
||||
sa.getTargets().add(depthsList.getFirst());
|
||||
@@ -132,7 +132,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
list = CardLists.filter(list, CardPredicates.isTargetableBy(sa));
|
||||
|
||||
CardCollection planeswalkerList = CardLists.filter(list, CardPredicates.Presets.PLANESWALKERS,
|
||||
CardPredicates.hasCounter(CounterType.LOYALTY, 5));
|
||||
CardPredicates.hasCounter(CounterEnumType.LOYALTY, 5));
|
||||
|
||||
if (!planeswalkerList.isEmpty()) {
|
||||
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
|
||||
@@ -159,11 +159,11 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
|
||||
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
|
||||
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
|
||||
CardPredicates.hasCounter(CounterType.ICE));
|
||||
CardPredicates.hasCounter(CounterEnumType.ICE));
|
||||
|
||||
if (!depthsList.isEmpty()) {
|
||||
Card depth = depthsList.getFirst();
|
||||
int ice = depth.getCounters(CounterType.ICE);
|
||||
int ice = depth.getCounters(CounterEnumType.ICE);
|
||||
if (amount >= ice) {
|
||||
sa.getTargets().add(depth);
|
||||
if (xPay) {
|
||||
@@ -180,7 +180,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
|
||||
CardCollection planeswalkerList = CardLists.filter(list,
|
||||
Predicates.and(CardPredicates.Presets.PLANESWALKERS, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
|
||||
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
|
||||
CardPredicates.hasLessCounter(CounterEnumType.LOYALTY, amount));
|
||||
|
||||
if (!planeswalkerList.isEmpty()) {
|
||||
Card best = ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList);
|
||||
@@ -196,7 +196,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
// do as M1M1 part
|
||||
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
||||
|
||||
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
|
||||
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.M1M1));
|
||||
|
||||
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, Keyword.PERSIST);
|
||||
if (!aiPersistList.isEmpty()) {
|
||||
@@ -209,7 +209,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// do as P1P1 part
|
||||
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasLessCounter(CounterType.P1P1, amount));
|
||||
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasLessCounter(CounterEnumType.P1P1, amount));
|
||||
CardCollection aiUndyingList = CardLists.getKeyword(aiP1P1List, Keyword.UNDYING);
|
||||
|
||||
if (!aiUndyingList.isEmpty()) {
|
||||
@@ -220,7 +220,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
// remove P1P1 counters from opposing creatures
|
||||
CardCollection oppP1P1List = CardLists.filter(list,
|
||||
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
|
||||
CardPredicates.hasCounter(CounterType.P1P1));
|
||||
CardPredicates.hasCounter(CounterEnumType.P1P1));
|
||||
if (!oppP1P1List.isEmpty()) {
|
||||
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(oppP1P1List));
|
||||
return true;
|
||||
@@ -244,7 +244,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
// no special amount for that one yet
|
||||
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
CardCollection aiList = CardLists.filterControlledBy(list, ai);
|
||||
aiList = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1, amount));
|
||||
aiList = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.M1M1, amount));
|
||||
|
||||
CardCollection aiPersist = CardLists.getKeyword(aiList, Keyword.PERSIST);
|
||||
if (!aiPersist.isEmpty()) {
|
||||
@@ -263,7 +263,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
// no special amount for that one yet
|
||||
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
|
||||
list = CardLists.filter(list, CardPredicates.hasCounter(CounterType.P1P1, amount));
|
||||
list = CardLists.filter(list, CardPredicates.hasCounter(CounterEnumType.P1P1, amount));
|
||||
|
||||
// currently only logic for Bloodcrazed Hoplite, but add logic for
|
||||
// targeting ai creatures too
|
||||
@@ -309,12 +309,12 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
}
|
||||
|
||||
CardCollection timeList = CardLists.filter(list, CardPredicates.hasLessCounter(CounterType.TIME, amount));
|
||||
CardCollection timeList = CardLists.filter(list, CardPredicates.hasLessCounter(CounterEnumType.TIME, amount));
|
||||
|
||||
if (!timeList.isEmpty()) {
|
||||
Card best = ComputerUtilCard.getBestAI(timeList);
|
||||
|
||||
int timeCount = best.getCounters(CounterType.TIME);
|
||||
int timeCount = best.getCounters(CounterEnumType.TIME);
|
||||
sa.getTargets().add(best);
|
||||
if (xPay) {
|
||||
source.setSVar("PayX", Integer.toString(timeCount));
|
||||
@@ -335,7 +335,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
CardCollection outlastCreats = CardLists.filter(list, CardPredicates.hasKeyword(Keyword.OUTLAST));
|
||||
if (!outlastCreats.isEmpty()) {
|
||||
// outlast cards often benefit from having +1/+1 counters, try not to remove last one
|
||||
CardCollection betterTargets = CardLists.filter(outlastCreats, CardPredicates.hasCounter(CounterType.P1P1, 2));
|
||||
CardCollection betterTargets = CardLists.filter(outlastCreats, CardPredicates.hasCounter(CounterEnumType.P1P1, 2));
|
||||
|
||||
if (!betterTargets.isEmpty()) {
|
||||
sa.getTargets().add(ComputerUtilCard.getWorstAI(betterTargets));
|
||||
@@ -363,7 +363,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#chooseNumber(forge.game.player.Player,
|
||||
* forge.game.spellability.SpellAbility, int, int, java.util.Map)
|
||||
*/
|
||||
@@ -377,8 +377,8 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
if (targetCard.getController().isOpponentOf(player)) {
|
||||
return !ComputerUtil.isNegativeCounter(type, targetCard) ? max : min;
|
||||
} else {
|
||||
if (targetCard.hasKeyword(Keyword.UNDYING) && type == CounterType.P1P1
|
||||
&& targetCard.getCounters(CounterType.P1P1) >= max) {
|
||||
if (targetCard.hasKeyword(Keyword.UNDYING) && type.is(CounterEnumType.P1P1)
|
||||
&& targetCard.getCounters(CounterEnumType.P1P1) >= max) {
|
||||
return max;
|
||||
}
|
||||
|
||||
@@ -387,9 +387,9 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
} else if (target instanceof Player) {
|
||||
Player targetPlayer = (Player) target;
|
||||
if (targetPlayer.isOpponentOf(player)) {
|
||||
return !type.equals(CounterType.POISON) ? max : min;
|
||||
return !type.equals(CounterEnumType.POISON) ? max : min;
|
||||
} else {
|
||||
return type.equals(CounterType.POISON) ? max : min;
|
||||
return type.equals(CounterEnumType.POISON) ? max : min;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +398,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
*
|
||||
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
|
||||
* forge.game.spellability.SpellAbility, java.util.Map)
|
||||
*/
|
||||
@@ -415,7 +415,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
if (targetCard.getController().isOpponentOf(ai)) {
|
||||
// if its a Planeswalker try to remove Loyality first
|
||||
if (targetCard.isPlaneswalker()) {
|
||||
return CounterType.LOYALTY;
|
||||
return CounterType.get(CounterEnumType.LOYALTY);
|
||||
}
|
||||
for (CounterType type : options) {
|
||||
if (!ComputerUtil.isNegativeCounter(type, targetCard)) {
|
||||
@@ -423,10 +423,10 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (options.contains(CounterType.M1M1) && targetCard.hasKeyword(Keyword.PERSIST)) {
|
||||
return CounterType.M1M1;
|
||||
} else if (options.contains(CounterType.P1P1) && targetCard.hasKeyword(Keyword.UNDYING)) {
|
||||
return CounterType.P1P1;
|
||||
if (options.contains(CounterType.get(CounterEnumType.M1M1)) && targetCard.hasKeyword(Keyword.PERSIST)) {
|
||||
return CounterType.get(CounterEnumType.M1M1);
|
||||
} else if (options.contains(CounterType.get(CounterEnumType.P1P1)) && targetCard.hasKeyword(Keyword.UNDYING)) {
|
||||
return CounterType.get(CounterEnumType.P1P1);
|
||||
}
|
||||
for (CounterType type : options) {
|
||||
if (ComputerUtil.isNegativeCounter(type, targetCard)) {
|
||||
@@ -438,13 +438,13 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
||||
Player targetPlayer = (Player) target;
|
||||
if (targetPlayer.isOpponentOf(ai)) {
|
||||
for (CounterType type : options) {
|
||||
if (!type.equals(CounterType.POISON)) {
|
||||
if (!type.equals(CounterEnumType.POISON)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (CounterType type : options) {
|
||||
if (type.equals(CounterType.POISON)) {
|
||||
if (type.equals(CounterEnumType.POISON)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
@@ -39,7 +39,7 @@ public class DamageAllAi extends SpellAbilityAi {
|
||||
if (!ai.getGame().getStack().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int x = -1;
|
||||
final String damage = sa.getParam("NumDmg");
|
||||
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
||||
@@ -50,7 +50,7 @@ public class DamageAllAi extends SpellAbilityAi {
|
||||
x = ComputerUtilMana.determineLeftoverMana(sa, ai);
|
||||
}
|
||||
if (damage.equals("ChosenX")) {
|
||||
x = source.getCounters(CounterType.LOYALTY);
|
||||
x = source.getCounters(CounterEnumType.LOYALTY);
|
||||
}
|
||||
if (x == -1) {
|
||||
if (determineOppToKill(ai, sa, source, dmg) != null) {
|
||||
|
||||
@@ -46,9 +46,9 @@ public class DamageDealAi extends DamageAiBase {
|
||||
if ("MadSarkhanDigDmg".equals(logic)) {
|
||||
return SpecialCardAi.SarkhanTheMad.considerDig(ai, sa);
|
||||
}
|
||||
|
||||
|
||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$ChosenNumber")) {
|
||||
int energy = ai.getCounters(CounterType.ENERGY);
|
||||
int energy = ai.getCounters(CounterEnumType.ENERGY);
|
||||
for (SpellAbility s : source.getSpellAbilities()) {
|
||||
if ("PayEnergy".equals(s.getParam("AILogic"))) {
|
||||
energy += AbilityUtils.calculateAmount(source, s.getParam("CounterNum"), sa);
|
||||
@@ -145,7 +145,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
if (sourceName.equals("Crater's Claws") && ai.hasFerocious()) {
|
||||
dmg += 2;
|
||||
}
|
||||
|
||||
|
||||
String logic = sa.getParamOrDefault("AILogic", "");
|
||||
if ("DiscardLands".equals(logic)) {
|
||||
dmg = 2;
|
||||
@@ -165,7 +165,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
List<Card> wolves = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), "Creature.Wolf+untapped+YouCtrl+Other", ai, source);
|
||||
dmg = Aggregates.sum(wolves, CardPredicates.Accessors.fnGetNetPower);
|
||||
} else if ("Triskelion".equals(logic)) {
|
||||
final int n = source.getCounters(CounterType.P1P1);
|
||||
final int n = source.getCounters(CounterEnumType.P1P1);
|
||||
if (n > 0) {
|
||||
if (ComputerUtil.playImmediately(ai, sa)) {
|
||||
/*
|
||||
@@ -196,9 +196,9 @@ public class DamageDealAi extends DamageAiBase {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (sourceName.equals("Sorin, Grim Nemesis")) {
|
||||
int loyalty = source.getCounters(CounterType.LOYALTY);
|
||||
int loyalty = source.getCounters(CounterEnumType.LOYALTY);
|
||||
for (; loyalty > 0; loyalty--) {
|
||||
if (this.damageTargetAI(ai, sa, loyalty, false)) {
|
||||
dmg = ComputerUtilCombat.getEnoughDamageToKill(sa.getTargetCard(), loyalty, source, false, false);
|
||||
@@ -228,7 +228,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if ("DiscardLands".equals(sa.getParam("AILogic")) && !ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
|
||||
return false;
|
||||
}
|
||||
@@ -309,7 +309,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
* <p>
|
||||
* dealDamageChooseTgtC.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param d
|
||||
* a int.
|
||||
* @param noPrevention
|
||||
@@ -445,7 +445,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
// As of right now, ranks planeswalkers by their Current Loyalty * 10 + Big buff if close to "Ultimate"
|
||||
int bestScore = 0;
|
||||
for (Card pw : pws) {
|
||||
int curLoyalty = pw.getCounters(CounterType.LOYALTY);
|
||||
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
||||
int pwScore = curLoyalty * 10;
|
||||
|
||||
for (SpellAbility sa : pw.getSpellAbilities()) {
|
||||
@@ -478,7 +478,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
|
||||
int bestScore = Integer.MAX_VALUE;
|
||||
for (Card pw : pws) {
|
||||
int curLoyalty = pw.getCounters(CounterType.LOYALTY);
|
||||
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
||||
|
||||
if (curLoyalty < bestScore) {
|
||||
bestScore = curLoyalty;
|
||||
@@ -515,7 +515,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
* <p>
|
||||
* damageTargetAI.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param saMe
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
* @param dmg
|
||||
@@ -543,7 +543,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
* <p>
|
||||
* damageChoosingTargets.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sa
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
* @param tgt
|
||||
@@ -587,7 +587,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
if (tgt.getMaxTargets(source, sa) <= 0 && !logic.equals("AssumeAtLeastOneTarget")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
immediately |= ComputerUtil.playImmediately(ai, sa);
|
||||
|
||||
if (!(sa.getParent() != null && sa.getParent().isTargetNumberValid())) {
|
||||
@@ -623,7 +623,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
continue;
|
||||
}
|
||||
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(humanCreature, dmg, source, false, noPrevention);
|
||||
if (assignedDamage <= dmg
|
||||
if (assignedDamage <= dmg
|
||||
&& humanCreature.getShieldCount() == 0 && !ComputerUtil.canRegenerate(humanCreature.getController(), humanCreature)) {
|
||||
tcs.add(humanCreature);
|
||||
tgt.addDividedAllocation(humanCreature, assignedDamage);
|
||||
@@ -756,7 +756,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else if (tgt.canTgtCreature() || tgt.canTgtPlaneswalker()) {
|
||||
final Card c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, enemy, mandatory);
|
||||
if (c != null) {
|
||||
@@ -825,8 +825,8 @@ public class DamageDealAi extends DamageAiBase {
|
||||
* <p>
|
||||
* damageChooseNontargeted.
|
||||
* </p>
|
||||
* @param ai
|
||||
*
|
||||
* @param ai
|
||||
*
|
||||
* @param saMe
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
* @param dmg
|
||||
@@ -881,7 +881,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
* <p>
|
||||
* damageChooseRequiredTargets.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sa
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
* @param tgt
|
||||
@@ -1006,7 +1006,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
// If I can kill my target by paying less mana, do it
|
||||
int actualPay = 0;
|
||||
final boolean noPrevention = sa.hasParam("NoPrevention");
|
||||
|
||||
|
||||
//target is a player
|
||||
if (!sa.getTargets().isTargetingAnyCard()) {
|
||||
actualPay = dmg;
|
||||
@@ -1037,15 +1037,15 @@ public class DamageDealAi extends DamageAiBase {
|
||||
|
||||
Player opponent = ai.getOpponents().min(PlayerPredicates.compareByLife());
|
||||
|
||||
// TODO: somehow account for the possible cost reduction?
|
||||
// TODO: somehow account for the possible cost reduction?
|
||||
int dmg = ComputerUtilMana.determineLeftoverMana(sa, ai, saTgt.getParam("XColor"));
|
||||
|
||||
|
||||
while (!ComputerUtilMana.canPayManaCost(sa, ai, dmg) && dmg > 0) {
|
||||
// TODO: ideally should never get here, currently put here as a precaution for complex mana base cases where the miscalculation might occur. Will remove later if it proves to never trigger.
|
||||
dmg--;
|
||||
System.out.println("Warning: AI could not pay mana cost for a XLifeDrain logic spell. Reducing X value to "+dmg);
|
||||
}
|
||||
|
||||
|
||||
// set the color map for black X for the purpose of Soul Burn
|
||||
// TODO: somehow generalize this calculation to allow other potential similar cards to function in the future
|
||||
if ("Soul Burn".equals(sourceName)) {
|
||||
@@ -1066,7 +1066,7 @@ public class DamageDealAi extends DamageAiBase {
|
||||
int toughness = c.getNetToughness();
|
||||
boolean canDie = !(c.hasKeyword(Keyword.INDESTRUCTIBLE) || ComputerUtil.canRegenerate(c.getController(), c));
|
||||
|
||||
// Currently will target creatures with toughness 3+ (or power 5+)
|
||||
// Currently will target creatures with toughness 3+ (or power 5+)
|
||||
// and only if the creature can actually die, do not "underdrain"
|
||||
// unless the creature has high power
|
||||
if (canDie && toughness <= dmg && ((toughness == dmg && toughness >= 3) || power >= 5)) {
|
||||
|
||||
@@ -101,7 +101,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
return SpecialCardAi.SarkhanTheMad.considerMakeDragon(ai, sa);
|
||||
} else if (logic != null && logic.startsWith("MinLoyalty.")) {
|
||||
int minLoyalty = Integer.parseInt(logic.substring(logic.indexOf(".") + 1));
|
||||
if (source.getCounters(CounterType.LOYALTY) < minLoyalty) {
|
||||
if (source.getCounters(CounterEnumType.LOYALTY) < minLoyalty) {
|
||||
return false;
|
||||
}
|
||||
} else if ("Polymorph".equals(logic)) {
|
||||
@@ -161,7 +161,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
//Check for undying
|
||||
return (!c.hasKeyword(Keyword.UNDYING) || c.getCounters(CounterType.P1P1) > 0);
|
||||
return (!c.hasKeyword(Keyword.UNDYING) || c.getCounters(CounterEnumType.P1P1) > 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -388,7 +388,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
if (CardLists.getNotType(list, "Creature").isEmpty()) {
|
||||
if (!sa.getUniqueTargets().isEmpty() && sa.getParent().getApi() == ApiType.Destroy
|
||||
&& sa.getUniqueTargets().get(0) instanceof Card) {
|
||||
// basic ai for Diaochan
|
||||
// basic ai for Diaochan
|
||||
c = (Card) sa.getUniqueTargets().get(0);
|
||||
} else {
|
||||
c = ComputerUtilCard.getWorstCreatureAI(list);
|
||||
@@ -413,7 +413,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
|
||||
Player tgtPlayer = tgtLand.getController();
|
||||
int oppLandsOTB = tgtPlayer.getLandsInPlay().size();
|
||||
|
||||
|
||||
// AI profile-dependent properties
|
||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||
int amountNoTempoCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK);
|
||||
@@ -436,7 +436,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
|
||||
// Non-basic lands are currently not ranked in any way in ComputerUtilCard#getBestLandAI, so if a non-basic land is best target,
|
||||
// consider killing it off unless there's too much potential tempo loss.
|
||||
// TODO: actually rank non-basics in that method and then kill off the potentially dangerous (manlands, Valakut) or lucrative
|
||||
// TODO: actually rank non-basics in that method and then kill off the potentially dangerous (manlands, Valakut) or lucrative
|
||||
// (dual/triple mana that opens access to a certain color) lands
|
||||
boolean nonBasicTgt = !tgtLand.isBasicLand();
|
||||
|
||||
@@ -448,7 +448,7 @@ public class DestroyAi extends SpellAbilityAi {
|
||||
boolean isHighPriority = highPriorityIfNoLandDrop && oppSkippedLandDrop;
|
||||
|
||||
boolean timingCheck = canManaLock || canColorLock || nonBasicTgt;
|
||||
boolean tempoCheck = numLandsOTB >= amountNoTempoCheck
|
||||
boolean tempoCheck = numLandsOTB >= amountNoTempoCheck
|
||||
|| ((numLandsInHand >= amountLandsInHand || isHighPriority) && ((numLandsInHand + numLandsOTB >= amountNoTimingCheck) || timingCheck));
|
||||
|
||||
// For Ghost Quarter, only use it if you have either more lands in play than your opponent
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
@@ -132,7 +134,7 @@ public class DigAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> valid, boolean isOptional, Player relatedPlayer) {
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> valid, boolean isOptional, Player relatedPlayer, Map<String, Object> params) {
|
||||
if ("DigForCreature".equals(sa.getParam("AILogic"))) {
|
||||
Card bestChoice = ComputerUtilCard.getBestCreatureAI(valid);
|
||||
if (bestChoice == null) {
|
||||
@@ -163,7 +165,7 @@ public class DigAi extends SpellAbilityAi {
|
||||
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
// an opponent choose a card from
|
||||
return Iterables.getFirst(options, null);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import forge.game.ability.ApiType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.cost.*;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
@@ -348,7 +349,7 @@ public class DrawAi extends SpellAbilityAi {
|
||||
}
|
||||
// try to make opponent lose to poison
|
||||
// currently only Caress of Phyrexia
|
||||
if (getPoison != null && oppA.canReceiveCounters(CounterType.POISON)) {
|
||||
if (getPoison != null && oppA.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
|
||||
if (oppA.getPoisonCounters() + numCards > 9) {
|
||||
sa.getTargets().add(oppA);
|
||||
return true;
|
||||
@@ -392,7 +393,7 @@ public class DrawAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
|
||||
if (getPoison != null && ai.canReceiveCounters(CounterType.POISON)) {
|
||||
if (getPoison != null && ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
|
||||
if (numCards + ai.getPoisonCounters() >= 8) {
|
||||
aiTarget = false;
|
||||
}
|
||||
@@ -451,7 +452,7 @@ public class DrawAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// ally would lose because of poison
|
||||
if (getPoison != null && ally.canReceiveCounters(CounterType.POISON)) {
|
||||
if (getPoison != null && ally.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
|
||||
if (ally.getPoisonCounters() + numCards > 9) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
@@ -84,7 +85,7 @@ public final class EncodeAi extends SpellAbilityAi {
|
||||
* forge.game.player.Player)
|
||||
*/
|
||||
@Override
|
||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
return chooseCard(ai, options, isOptional);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
@@ -23,7 +25,7 @@ public class LegendaryRuleAi extends SpellAbilityAi {
|
||||
|
||||
|
||||
@Override
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
// Choose a single legendary/planeswalker card to keep
|
||||
Card firstOption = Iterables.getFirst(options, null);
|
||||
boolean choosingFromPlanewalkers = firstOption.isPlaneswalker();
|
||||
@@ -38,16 +40,16 @@ public class LegendaryRuleAi extends SpellAbilityAi {
|
||||
if (firstOption.getName().equals("Dark Depths")) {
|
||||
Card best = firstOption;
|
||||
for (Card c : options) {
|
||||
if (c.getCounters(CounterType.ICE) < best.getCounters(CounterType.ICE)) {
|
||||
if (c.getCounters(CounterEnumType.ICE) < best.getCounters(CounterEnumType.ICE)) {
|
||||
best = c;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
} else if (firstOption.getCounters(CounterType.KI) > 0) {
|
||||
} else if (firstOption.getCounters(CounterEnumType.KI) > 0) {
|
||||
// Extra Rule for KI counter
|
||||
Card best = firstOption;
|
||||
for (Card c : options) {
|
||||
if (c.getCounters(CounterType.KI) > best.getCounters(CounterType.KI)) {
|
||||
if (c.getCounters(CounterEnumType.KI) > best.getCounters(CounterEnumType.KI)) {
|
||||
best = c;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import forge.ai.ComputerUtilMana;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -130,7 +130,7 @@ public class LifeSetAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (sourceName.equals("Eternity Vessel")
|
||||
&& (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterType.CHARGE) == 0))) {
|
||||
&& (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterEnumType.CHARGE) == 0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ public class ManaEffectAi extends SpellAbilityAi {
|
||||
int manaSurplus = 0;
|
||||
if ("XChoice".equals(host.getSVar("X"))
|
||||
&& sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class)) {
|
||||
CounterType ctrType = CounterType.KI; // Petalmane Baku
|
||||
CounterType ctrType = CounterType.get(CounterEnumType.KI); // Petalmane Baku
|
||||
for (CostPart part : sa.getPayCosts().getCostParts()) {
|
||||
if (part instanceof CostRemoveCounter) {
|
||||
ctrType = ((CostRemoveCounter)part).counter;
|
||||
|
||||
@@ -18,6 +18,7 @@ import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
@@ -167,7 +168,7 @@ public class MustBlockAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
|
||||
Player targetedPlayer) {
|
||||
Player targetedPlayer, Map<String, Object> params) {
|
||||
final Card host = sa.getHostCard();
|
||||
|
||||
Card attacker = host;
|
||||
|
||||
@@ -20,6 +20,7 @@ import forge.game.zone.ZoneType;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PlayAi extends SpellAbilityAi {
|
||||
|
||||
@@ -88,7 +89,7 @@ public class PlayAi extends SpellAbilityAi {
|
||||
minCMC = sa.getPayCosts().getTotalMana().getCMC();
|
||||
}
|
||||
validOpts = CardLists.filter(validOpts, CardPredicates.greaterCMC(minCMC));
|
||||
return chooseSingleCard(ai, sa, validOpts, sa.hasParam("Optional"), null) != null;
|
||||
return chooseSingleCard(ai, sa, validOpts, sa.hasParam("Optional"), null, null) != null;
|
||||
}
|
||||
|
||||
if (source != null && source.hasKeyword(Keyword.HIDEAWAY) && source.hasRemembered()) {
|
||||
@@ -142,8 +143,7 @@ public class PlayAi extends SpellAbilityAi {
|
||||
*/
|
||||
@Override
|
||||
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options,
|
||||
final boolean isOptional,
|
||||
Player targetedPlayer) {
|
||||
final boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.google.common.base.Predicate;
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
@@ -59,7 +60,7 @@ public class PoisonAi extends SpellAbilityAi {
|
||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
if (sa.usesTargeting()) {
|
||||
return tgtPlayer(ai, sa, mandatory);
|
||||
} else if (mandatory || !ai.canReceiveCounters(CounterType.POISON)) {
|
||||
} else if (mandatory || !ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
|
||||
// mandatory or ai is uneffected
|
||||
return true;
|
||||
} else {
|
||||
@@ -92,7 +93,7 @@ public class PoisonAi extends SpellAbilityAi {
|
||||
public boolean apply(Player input) {
|
||||
if (input.cantLose()) {
|
||||
return false;
|
||||
} else if (!input.canReceiveCounters(CounterType.POISON)) {
|
||||
} else if (!input.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -113,7 +114,7 @@ public class PoisonAi extends SpellAbilityAi {
|
||||
if (tgts.isEmpty()) {
|
||||
if (mandatory) {
|
||||
// AI is uneffected
|
||||
if (ai.canBeTargetedBy(sa) && ai.canReceiveCounters(CounterType.POISON)) {
|
||||
if (ai.canBeTargetedBy(sa) && ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
|
||||
sa.getTargets().add(ai);
|
||||
return true;
|
||||
}
|
||||
@@ -127,7 +128,7 @@ public class PoisonAi extends SpellAbilityAi {
|
||||
if (input.cantLose()) {
|
||||
return true;
|
||||
}
|
||||
return !input.canReceiveCounters(CounterType.POISON);
|
||||
return !input.canReceiveCounters(CounterType.get(CounterEnumType.POISON));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -150,7 +150,7 @@ public class PumpAi extends PumpAiBase {
|
||||
}
|
||||
|
||||
final String counterType = moveSA.getParam("CounterType");
|
||||
final CounterType cType = "Any".equals(counterType) ? null : CounterType.valueOf(counterType);
|
||||
final CounterType cType = "Any".equals(counterType) ? null : CounterType.getType(counterType);
|
||||
|
||||
final PhaseHandler ph = game.getPhaseHandler();
|
||||
if (ph.inCombat() && ph.getPlayerTurn().isOpponentOf(ai)) {
|
||||
@@ -185,7 +185,7 @@ public class PumpAi extends PumpAiBase {
|
||||
// cant use substract on Copy
|
||||
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
|
||||
|
||||
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||
if (CounterEnumType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|
||||
|| card.isToken();
|
||||
}
|
||||
@@ -235,7 +235,7 @@ public class PumpAi extends PumpAiBase {
|
||||
// cant use substract on Copy
|
||||
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
|
||||
|
||||
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||
if (CounterEnumType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
|
||||
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|
||||
|| card.isToken();
|
||||
}
|
||||
@@ -402,7 +402,7 @@ public class PumpAi extends PumpAiBase {
|
||||
|
||||
if ("DebuffForXCounters".equals(sa.getParam("AILogic")) && sa.getTargetCard() != null) {
|
||||
// e.g. Skullmane Baku
|
||||
CounterType ctrType = CounterType.KI;
|
||||
CounterType ctrType = CounterType.get(CounterEnumType.KI);
|
||||
for (CostPart part : sa.getPayCosts().getCostParts()) {
|
||||
if (part instanceof CostRemoveCounter) {
|
||||
ctrType = ((CostRemoveCounter)part).counter;
|
||||
@@ -730,7 +730,7 @@ public class PumpAi extends PumpAiBase {
|
||||
final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : "";
|
||||
|
||||
if (numDefense.equals("-X") && sa.getSVar("X").equals("Count$ChosenNumber")) {
|
||||
int energy = ai.getCounters(CounterType.ENERGY);
|
||||
int energy = ai.getCounters(CounterEnumType.ENERGY);
|
||||
for (SpellAbility s : source.getSpellAbilities()) {
|
||||
if ("PayEnergy".equals(s.getParam("AILogic"))) {
|
||||
energy += AbilityUtils.calculateAmount(source, s.getParam("CounterNum"), sa);
|
||||
@@ -860,7 +860,7 @@ public class PumpAi extends PumpAiBase {
|
||||
final boolean isInfect = source.hasKeyword(Keyword.INFECT); // Flesh-Eater Imp
|
||||
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
|
||||
|
||||
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.POISON)) {
|
||||
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
|
||||
lethalDmg = Integer.MAX_VALUE; // won't be able to deal poison damage to kill the opponent
|
||||
}
|
||||
|
||||
@@ -976,7 +976,7 @@ public class PumpAi extends PumpAiBase {
|
||||
final boolean isInfect = source.hasKeyword(Keyword.INFECT);
|
||||
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
|
||||
|
||||
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.POISON)) {
|
||||
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
|
||||
lethalDmg = Integer.MAX_VALUE; // won't be able to deal poison damage to kill the opponent
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import forge.game.zone.ZoneType;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class RepeatEachAi extends SpellAbilityAi {
|
||||
@@ -118,7 +119,7 @@ public class RepeatEachAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
|
||||
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
return ComputerUtilCard.getBestCreatureAI(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public class ScryAi extends SpellAbilityAi {
|
||||
} else if ("BrainJar".equals(aiLogic)) {
|
||||
final Card source = sa.getHostCard();
|
||||
|
||||
int counterNum = source.getCounters(CounterType.CHARGE);
|
||||
int counterNum = source.getCounters(CounterEnumType.CHARGE);
|
||||
// no need for logic
|
||||
if (counterNum == 0) {
|
||||
return false;
|
||||
|
||||
@@ -248,7 +248,7 @@ public class SetStateAi extends SpellAbilityAi {
|
||||
final Card othercard = aiPlayer.getCardsIn(ZoneType.Battlefield, other.getName()).getFirst();
|
||||
|
||||
// for legendary KI counter creatures
|
||||
if (othercard.getCounters(CounterType.KI) >= source.getCounters(CounterType.KI)) {
|
||||
if (othercard.getCounters(CounterEnumType.KI) >= source.getCounters(CounterEnumType.KI)) {
|
||||
// if the other legendary is useless try to replace it
|
||||
return ComputerUtilCard.isUselessCreature(aiPlayer, othercard);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package forge.ai.ability;
|
||||
import forge.ai.*;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostPart;
|
||||
@@ -68,7 +69,7 @@ public class TapAi extends TapAiBase {
|
||||
} else {
|
||||
if ("TapForXCounters".equals(sa.getParam("AILogic"))) {
|
||||
// e.g. Waxmane Baku
|
||||
CounterType ctrType = CounterType.KI;
|
||||
CounterType ctrType = CounterType.get(CounterEnumType.KI);
|
||||
for (CostPart part : sa.getPayCosts().getCostParts()) {
|
||||
if (part instanceof CostRemoveCounter) {
|
||||
ctrType = ((CostRemoveCounter)part).counter;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import forge.ai.*;
|
||||
@@ -296,7 +298,7 @@ public class TokenAi extends SpellAbilityAi {
|
||||
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.player.Player> options)
|
||||
*/
|
||||
@Override
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
Combat combat = ai.getGame().getCombat();
|
||||
// TokenAttacking
|
||||
if (combat != null && sa.hasParam("TokenAttacking")) {
|
||||
@@ -314,7 +316,7 @@ public class TokenAi extends SpellAbilityAi {
|
||||
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayerOrPlaneswalker(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.GameEntity> options)
|
||||
*/
|
||||
@Override
|
||||
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
|
||||
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
|
||||
Combat combat = ai.getGame().getCombat();
|
||||
// TokenAttacking
|
||||
if (combat != null && sa.hasParam("TokenAttacking")) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class UntapAi extends SpellAbilityAi {
|
||||
@Override
|
||||
@@ -311,7 +312,7 @@ public class UntapAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> list, boolean isOptional, Player targetedPlayer) {
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> list, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
PlayerCollection pl = new PlayerCollection();
|
||||
pl.add(ai);
|
||||
pl.addAll(ai.getAllies());
|
||||
|
||||
@@ -76,7 +76,7 @@ public class GameCopier {
|
||||
newPlayer.addSpellCastThisTurn();
|
||||
for (int j = 0; j < origPlayer.getLandsPlayedThisTurn(); j++)
|
||||
newPlayer.addLandPlayedThisTurn();
|
||||
newPlayer.setCounters(Maps.newEnumMap(origPlayer.getCounters()));
|
||||
newPlayer.setCounters(Maps.newHashMap(origPlayer.getCounters()));
|
||||
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
|
||||
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
|
||||
newPlayer.setPreventNextDamage(origPlayer.getPreventNextDamage());
|
||||
@@ -328,7 +328,7 @@ public class GameCopier {
|
||||
|
||||
Map<CounterType, Integer> counters = c.getCounters();
|
||||
if (!counters.isEmpty()) {
|
||||
newCard.setCounters(Maps.newEnumMap(counters));
|
||||
newCard.setCounters(Maps.newHashMap(counters));
|
||||
}
|
||||
if (c.getChosenPlayer() != null) {
|
||||
newCard.setChosenPlayer(playerMap.get(c.getChosenPlayer()));
|
||||
|
||||
@@ -3,7 +3,7 @@ package forge.ai.simulation;
|
||||
import forge.ai.CreatureEvaluator;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -154,7 +154,7 @@ public class GameStateEvaluator {
|
||||
// e.g. a 5 CMC permanent results in 200, whereas a 5/5 creature is ~225
|
||||
int value = 50 + 30 * c.getCMC();
|
||||
if (c.isPlaneswalker()) {
|
||||
value += 2 * c.getCounters(CounterType.LOYALTY);
|
||||
value += 2 * c.getCounters(CounterEnumType.LOYALTY);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementResult;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityPredicates;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
@@ -180,6 +179,10 @@ public class GameAction {
|
||||
if (lastKnownInfo == null) {
|
||||
lastKnownInfo = CardUtil.getLKICopy(c);
|
||||
}
|
||||
|
||||
if (!lastKnownInfo.hasKeyword("Counters remain on CARDNAME as it moves to any zone other than a player's hand or library.") || zoneTo.is(ZoneType.Hand) || zoneTo.is(ZoneType.Library)) {
|
||||
copied.clearCounters();
|
||||
}
|
||||
} else {
|
||||
// if from Battlefield to Graveyard and Card does exist in LastStateBattlefield
|
||||
// use that instead
|
||||
@@ -228,17 +231,14 @@ public class GameAction {
|
||||
for (final StaticAbility sa : copied.getStaticAbilities()) {
|
||||
sa.setHostCard(copied);
|
||||
}
|
||||
if (c.getName().equals("Skullbriar, the Walking Grave")) {
|
||||
copied.setCounters(c.getCounters());
|
||||
}
|
||||
|
||||
// ensure that any leftover keyword/type changes are cleared in the state view
|
||||
copied.updateStateForView();
|
||||
} else { //Token
|
||||
copied = c;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that any leftover keyword/type changes are cleared in the state view
|
||||
copied.updateStateForView();
|
||||
|
||||
// Clean up temporary variables such as Sunburst value or announced PayX value
|
||||
if (!(zoneTo.is(ZoneType.Stack) || zoneTo.is(ZoneType.Battlefield))) {
|
||||
copied.clearTemporaryVars();
|
||||
@@ -421,13 +421,6 @@ public class GameAction {
|
||||
return copied;
|
||||
}
|
||||
|
||||
// remove all counters from the card if destination is not the battlefield
|
||||
// UNLESS we're dealing with Skullbriar, the Walking Grave
|
||||
if (!c.isToken() && (zoneTo.is(ZoneType.Hand) || zoneTo.is(ZoneType.Library) ||
|
||||
(!toBattlefield && !c.getName().equals("Skullbriar, the Walking Grave")))) {
|
||||
copied.clearCounters();
|
||||
}
|
||||
|
||||
if (!c.isToken() && !toBattlefield) {
|
||||
copied.clearDevoured();
|
||||
copied.clearDelved();
|
||||
@@ -1030,8 +1023,10 @@ public class GameAction {
|
||||
|
||||
checkAgain |= stateBasedAction704_5r(c); // annihilate +1/+1 counters with -1/-1 ones
|
||||
|
||||
if (c.getCounters(CounterType.DREAM) > 7 && c.hasKeyword("CARDNAME can't have more than seven dream counters on it.")) {
|
||||
c.subtractCounter(CounterType.DREAM, c.getCounters(CounterType.DREAM) - 7);
|
||||
final CounterType dreamType = CounterType.get(CounterEnumType.DREAM);
|
||||
|
||||
if (c.getCounters(dreamType) > 7 && c.hasKeyword("CARDNAME can't have more than seven dream counters on it.")) {
|
||||
c.subtractCounter(dreamType, c.getCounters(dreamType) - 7);
|
||||
checkAgain = true;
|
||||
}
|
||||
}
|
||||
@@ -1123,7 +1118,7 @@ public class GameAction {
|
||||
if (!c.canBeSacrificed()) {
|
||||
return false;
|
||||
}
|
||||
if (c.getCounters(CounterType.LORE) < c.getFinalChapterNr()) {
|
||||
if (c.getCounters(CounterEnumType.LORE) < c.getFinalChapterNr()) {
|
||||
return false;
|
||||
}
|
||||
if (!game.getStack().hasSimultaneousStackEntries() &&
|
||||
@@ -1164,16 +1159,18 @@ public class GameAction {
|
||||
|
||||
private boolean stateBasedAction704_5r(Card c) {
|
||||
boolean checkAgain = false;
|
||||
int plusOneCounters = c.getCounters(CounterType.P1P1);
|
||||
int minusOneCounters = c.getCounters(CounterType.M1M1);
|
||||
final CounterType p1p1 = CounterType.get(CounterEnumType.P1P1);
|
||||
final CounterType m1m1 = CounterType.get(CounterEnumType.M1M1);
|
||||
int plusOneCounters = c.getCounters(p1p1);
|
||||
int minusOneCounters = c.getCounters(m1m1);
|
||||
if (plusOneCounters > 0 && minusOneCounters > 0) {
|
||||
int remove = Math.min(plusOneCounters, minusOneCounters);
|
||||
// If a permanent has both a +1/+1 counter and a -1/-1 counter on it,
|
||||
// N +1/+1 and N -1/-1 counters are removed from it, where N is the
|
||||
// smaller of the number of +1/+1 and -1/-1 counters on it.
|
||||
// This should fire remove counters trigger
|
||||
c.subtractCounter(CounterType.P1P1, remove);
|
||||
c.subtractCounter(CounterType.M1M1, remove);
|
||||
c.subtractCounter(p1p1, remove);
|
||||
c.subtractCounter(m1m1, remove);
|
||||
checkAgain = true;
|
||||
}
|
||||
return checkAgain;
|
||||
@@ -1294,7 +1291,7 @@ public class GameAction {
|
||||
//final Multimap<String, Card> uniqueWalkers = ArrayListMultimap.create(); // Not used as of Ixalan
|
||||
|
||||
for (Card c : list) {
|
||||
if (c.getCounters(CounterType.LOYALTY) <= 0) {
|
||||
if (c.getCounters(CounterEnumType.LOYALTY) <= 0) {
|
||||
sacrificeDestroy(c, null, table);
|
||||
// Play the Destroy sound
|
||||
game.fireEvent(new GameEventCardDestroyed());
|
||||
@@ -1354,7 +1351,8 @@ public class GameAction {
|
||||
|
||||
recheck = true;
|
||||
|
||||
Card toKeep = p.getController().chooseSingleEntityForEffect(new CardCollection(cc), new AbilitySub(ApiType.InternalLegendaryRule, null, null, null), "You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)");
|
||||
Card toKeep = p.getController().chooseSingleEntityForEffect(new CardCollection(cc), new SpellAbility.EmptySa(ApiType.InternalLegendaryRule, null, p),
|
||||
"You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)", null);
|
||||
for (Card c: cc) {
|
||||
if (c != toKeep) {
|
||||
sacrificeDestroy(c, null, table);
|
||||
|
||||
@@ -472,7 +472,7 @@ public final class GameActionUtil {
|
||||
CardFactoryUtil.setupETBReplacementAbility(saAb);
|
||||
|
||||
String desc = "It enters the battlefield with ";
|
||||
desc += Lang.nounWithNumeral(v, CounterType.P1P1.getName() + " counter");
|
||||
desc += Lang.nounWithNumeral(v, CounterEnumType.P1P1.getName() + " counter");
|
||||
desc += " on it.";
|
||||
|
||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
||||
|
||||
@@ -24,6 +24,7 @@ import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.event.GameEventCardAttachment;
|
||||
import forge.game.keyword.Keyword;
|
||||
@@ -38,6 +39,7 @@ import forge.util.collect.FCollection;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@@ -48,7 +50,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
private int preventNextDamage = 0;
|
||||
protected CardCollection attachedCards;
|
||||
private Map<Card, Map<String, String>> preventionShieldsWithEffects = Maps.newTreeMap();
|
||||
protected Map<CounterType, Integer> counters = Maps.newEnumMap(CounterType.class);
|
||||
protected Map<CounterType, Integer> counters = Maps.newTreeMap();
|
||||
|
||||
protected GameEntity(int id0) {
|
||||
id = id0;
|
||||
@@ -315,7 +317,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
}
|
||||
|
||||
// enchanted means attached by Aura
|
||||
return CardLists.count(attachedCards, CardPredicates.Presets.AURA) > 0;
|
||||
return Iterables.any(attachedCards, CardPredicates.Presets.AURA);
|
||||
}
|
||||
|
||||
public final boolean hasCardAttachment(Card c) {
|
||||
@@ -453,6 +455,10 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
return value == null ? 0 : value;
|
||||
}
|
||||
|
||||
public final int getCounters(final CounterEnumType counterType) {
|
||||
return getCounters(CounterType.get(counterType));
|
||||
}
|
||||
|
||||
public void setCounters(final CounterType counterType, final Integer num) {
|
||||
if (num <= 0) {
|
||||
counters.remove(counterType);
|
||||
@@ -461,6 +467,10 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setCounters(final CounterEnumType counterType, final Integer num) {
|
||||
setCounters(CounterType.get(counterType), num);
|
||||
}
|
||||
|
||||
abstract public void setCounters(final Map<CounterType, Integer> allCounters);
|
||||
|
||||
abstract public boolean canReceiveCounters(final CounterType type);
|
||||
@@ -468,6 +478,16 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
abstract public void subtractCounter(final CounterType counterName, final int n);
|
||||
abstract public void clearCounters();
|
||||
|
||||
public boolean canReceiveCounters(final CounterEnumType type) {
|
||||
return canReceiveCounters(CounterType.get(type));
|
||||
}
|
||||
|
||||
public int addCounter(final CounterEnumType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table) {
|
||||
return addCounter(CounterType.get(counterType), n, source, applyMultiplier, fireEvents, table);
|
||||
}
|
||||
public void subtractCounter(final CounterEnumType counterName, final int n) {
|
||||
subtractCounter(CounterType.get(counterName), n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
|
||||
@@ -95,7 +95,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
String action = event.sa.isSpell() ? localizer.getMessage("lblCast")
|
||||
: event.sa.isTrigger() ? localizer.getMessage("lblTriggered")
|
||||
: localizer.getMessage("lblActivated");
|
||||
String object = event.sa.getStackDescription().startsWith("Morph ")
|
||||
String object = event.si.getStackDescription().startsWith("Morph ")
|
||||
? localizer.getMessage("lblMorph")
|
||||
: event.sa.getHostCard().toString();
|
||||
|
||||
|
||||
@@ -53,8 +53,9 @@ public class AbilityUtils {
|
||||
if ("ReplacedCounterType".equals(name)) {
|
||||
name = (String) sa.getReplacingObject(AbilityKey.CounterType);
|
||||
}
|
||||
try {
|
||||
//try {
|
||||
counterType = CounterType.getType(name);
|
||||
/*
|
||||
} catch (Exception e) {
|
||||
String type = sa.getSVar(name);
|
||||
if (type.equals("")) {
|
||||
@@ -66,6 +67,7 @@ public class AbilityUtils {
|
||||
}
|
||||
counterType = CounterType.getType(type);
|
||||
}
|
||||
//*/
|
||||
|
||||
return counterType;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.google.common.collect.Lists;
|
||||
import forge.GameCommand;
|
||||
import forge.card.CardType;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
@@ -34,7 +35,7 @@ import forge.util.collect.FCollection;
|
||||
* <p>
|
||||
* AbilityFactory_AlterLife class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: AbilityFactoryAlterLife.java 17656 2012-10-22 19:32:56Z Max mtg $
|
||||
*/
|
||||
@@ -93,7 +94,7 @@ public abstract class SpellAbilityEffect {
|
||||
final String baseDesc = this.getStackDescription(sa);
|
||||
if (conditionDesc != null) {
|
||||
sb.append(conditionDesc).append(" ");
|
||||
}
|
||||
}
|
||||
sb.append(baseDesc);
|
||||
}
|
||||
|
||||
@@ -125,7 +126,7 @@ public abstract class SpellAbilityEffect {
|
||||
/**
|
||||
* Append the description of a {@link SpellAbility} to a
|
||||
* {@link StringBuilder}.
|
||||
*
|
||||
*
|
||||
* @param sa
|
||||
* a {@link SpellAbility}.
|
||||
* @param sb
|
||||
@@ -173,7 +174,7 @@ public abstract class SpellAbilityEffect {
|
||||
|
||||
private static CardCollection getCards(final boolean definedFirst, final String definedParam, final SpellAbility sa) {
|
||||
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam));
|
||||
return useTargets ? new CardCollection(sa.getTargets().getTargetCards())
|
||||
return useTargets ? new CardCollection(sa.getTargets().getTargetCards())
|
||||
: AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam(definedParam), sa);
|
||||
}
|
||||
|
||||
@@ -196,10 +197,22 @@ public abstract class SpellAbilityEffect {
|
||||
|
||||
private static List<SpellAbility> getSpells(final boolean definedFirst, final String definedParam, final SpellAbility sa) {
|
||||
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam));
|
||||
return useTargets ? Lists.newArrayList(sa.getTargets().getTargetSpells())
|
||||
return useTargets ? Lists.newArrayList(sa.getTargets().getTargetSpells())
|
||||
: AbilityUtils.getDefinedSpellAbilities(sa.getHostCard(), sa.getParam(definedParam), sa);
|
||||
}
|
||||
|
||||
|
||||
// Targets of card or player type
|
||||
protected final static List<GameEntity> getTargetEntities(final SpellAbility sa) { return getEntities(false, "Defined", sa); }
|
||||
protected final static List<GameEntity> getTargetEntities(final SpellAbility sa, final String definedParam) { return getEntities(false, definedParam, sa); }
|
||||
protected final static List<GameEntity> getDefinedEntitiesOrTargeted(SpellAbility sa, final String definedParam) { return getEntities(true, definedParam, sa); }
|
||||
|
||||
private static List<GameEntity> getEntities(final boolean definedFirst, final String definedParam, final SpellAbility sa) {
|
||||
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam));
|
||||
return useTargets ? Lists.newArrayList(sa.getTargets().getTargetEntities())
|
||||
: AbilityUtils.getDefinedEntities(sa.getHostCard(), sa.getParam(definedParam), sa);
|
||||
}
|
||||
|
||||
// Targets of unspecified type
|
||||
protected final static List<GameObject> getTargets(final SpellAbility sa) { return getTargetables(false, "Defined", sa); }
|
||||
protected final static List<GameObject> getTargets(final SpellAbility sa, final String definedParam) { return getTargetables(false, definedParam, sa); }
|
||||
@@ -207,10 +220,10 @@ public abstract class SpellAbilityEffect {
|
||||
|
||||
private static List<GameObject> getTargetables(final boolean definedFirst, final String definedParam, final SpellAbility sa) {
|
||||
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam));
|
||||
return useTargets ? Lists.newArrayList(sa.getTargets().getTargets())
|
||||
return useTargets ? Lists.newArrayList(sa.getTargets().getTargets())
|
||||
: AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam(definedParam), sa);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected static void registerDelayedTrigger(final SpellAbility sa, String location, final List<Card> crds) {
|
||||
boolean intrinsic = sa.isIntrinsic();
|
||||
@@ -218,14 +231,14 @@ public abstract class SpellAbilityEffect {
|
||||
boolean combat = location.endsWith("Combat");
|
||||
|
||||
String desc = sa.hasParam("AtEOTDesc") ? sa.getParam("AtEOTDesc") : "";
|
||||
|
||||
|
||||
if (your) {
|
||||
location = location.substring("Your".length());
|
||||
}
|
||||
if (combat) {
|
||||
location = location.substring(0, location.length() - "Combat".length());
|
||||
}
|
||||
|
||||
|
||||
if (desc.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (location.equals("Hand")) {
|
||||
@@ -253,12 +266,12 @@ public abstract class SpellAbilityEffect {
|
||||
StringBuilder delTrig = new StringBuilder();
|
||||
delTrig.append("Mode$ Phase | Phase$ ");
|
||||
delTrig.append(combat ? "EndCombat " : "End Of Turn ");
|
||||
|
||||
|
||||
if (your) {
|
||||
delTrig.append("| ValidPlayer$ You ");
|
||||
}
|
||||
delTrig.append("| TriggerDescription$ ").append(desc);
|
||||
|
||||
|
||||
final Trigger trig = TriggerHandler.parseTrigger(delTrig.toString(), sa.getHostCard(), intrinsic);
|
||||
for (final Card c : crds) {
|
||||
trig.addRemembered(c);
|
||||
@@ -289,7 +302,7 @@ public abstract class SpellAbilityEffect {
|
||||
trig.setOverridingAbility(newSa);
|
||||
sa.getActivatingPlayer().getGame().getTriggerHandler().registerDelayedTrigger(trig);
|
||||
}
|
||||
|
||||
|
||||
protected static void addSelfTrigger(final SpellAbility sa, String location, final Card card) {
|
||||
|
||||
String trigStr = "Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield " +
|
||||
@@ -311,17 +324,17 @@ public abstract class SpellAbilityEffect {
|
||||
card.setSVar("EndOfTurnLeavePlay", "AtEOT");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static void addForgetOnMovedTrigger(final Card card, final String zone) {
|
||||
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ " + zone + " | Destination$ Any | TriggerZones$ Command | Static$ True";
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, card);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, card);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
|
||||
@@ -336,22 +349,22 @@ public abstract class SpellAbilityEffect {
|
||||
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
|
||||
addedTrigger.setIntrinsic(true);
|
||||
}
|
||||
|
||||
|
||||
protected static void addForgetCounterTrigger(final Card card, final String counterType) {
|
||||
String trig = "Mode$ CounterRemoved | TriggerZones$ Command | ValidCard$ Card.IsRemembered | CounterType$ " + counterType + " | NewCounterAmount$ 0 | Static$ True";
|
||||
|
||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
||||
|
||||
|
||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, card);
|
||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, card);
|
||||
saForget.setSubAbility(saExile);
|
||||
|
||||
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true);
|
||||
parsedTrigger.setOverridingAbility(saForget);
|
||||
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
|
||||
addedTrigger.setIntrinsic(true);
|
||||
addedTrigger.setIntrinsic(true);
|
||||
}
|
||||
|
||||
protected static void addLeaveBattlefieldReplacement(final Card card, final SpellAbility sa, final String zone) {
|
||||
@@ -378,10 +391,10 @@ public abstract class SpellAbilityEffect {
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa);
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
}
|
||||
|
||||
|
||||
protected static void addLeaveBattlefieldReplacement(final Card eff, final String zone) {
|
||||
final String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered "
|
||||
+ "| Origin$ Battlefield | ExcludeDestination$ " + zone
|
||||
+ "| Origin$ Battlefield | ExcludeDestination$ " + zone
|
||||
+ "| Description$ If Creature would leave the battlefield, "
|
||||
+ " exile it instead of putting it anywhere else.";
|
||||
String effect = "DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ " + zone;
|
||||
@@ -392,7 +405,7 @@ public abstract class SpellAbilityEffect {
|
||||
re.setOverridingAbility(AbilityFactory.getAbility(effect, eff));
|
||||
eff.addReplacementEffect(re);
|
||||
}
|
||||
|
||||
|
||||
// create a basic template for Effect to be used somewhere else
|
||||
protected static Card createEffect(final SpellAbility sa, final Player controller, final String name,
|
||||
final String image) {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -10,6 +14,7 @@ import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.token.TokenInfo;
|
||||
import forge.game.event.GameEventCombatChanged;
|
||||
@@ -79,13 +84,16 @@ public class AmassEffect extends TokenEffectBase {
|
||||
game.updateCombatForView();
|
||||
game.fireEvent(new GameEventCombatChanged());
|
||||
}
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", CounterType.get(CounterEnumType.P1P1));
|
||||
params.put("Amount", 1);
|
||||
|
||||
CardCollectionView tgtCards = CardLists.getType(activator.getCardsIn(ZoneType.Battlefield), "Army");
|
||||
tgtCards = pc.chooseCardsForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblChooseAnArmy"), 1, 1, false);
|
||||
tgtCards = pc.chooseCardsForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblChooseAnArmy"), 1, 1, false, params);
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
for(final Card tgtCard : tgtCards) {
|
||||
tgtCard.addCounter(CounterType.P1P1, amount, activator, true, table);
|
||||
tgtCard.addCounter(CounterEnumType.P1P1, amount, activator, true, table);
|
||||
game.updateLastStateForCard(tgtCard);
|
||||
|
||||
if (remember) {
|
||||
|
||||
@@ -35,7 +35,8 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
sa.setHostCard(c);
|
||||
}
|
||||
|
||||
Card source = sa.getHostCard();
|
||||
final Card source = sa.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
|
||||
CardCollection attachments;
|
||||
final List<GameObject> targets = getDefinedOrTargeted(sa, "Defined");
|
||||
@@ -57,15 +58,22 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
|
||||
final Player p = sa.getActivatingPlayer();
|
||||
|
||||
if (sa.hasParam("Object")) {
|
||||
attachments = AbilityUtils.getDefinedCards(source, sa.getParam("Object"), sa);
|
||||
if (sa.hasParam("ChooseAnObject")) {
|
||||
Card c = p.getController().chooseSingleEntityForEffect(attachments, sa, sa.getParam("ChooseAnObject"));
|
||||
attachments.clear();
|
||||
if (c != null) {
|
||||
attachments.add(c);
|
||||
}
|
||||
if (sa.hasParam("Choices")) {
|
||||
ZoneType choiceZone = ZoneType.Battlefield;
|
||||
if (sa.hasParam("ChoiceZone")) {
|
||||
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
|
||||
}
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoose") + " ";
|
||||
|
||||
CardCollection choices = CardLists.getValidCards(game.getCardsIn(choiceZone), sa.getParam("Choices"), p, source, sa);
|
||||
|
||||
Card c = p.getController().chooseSingleEntityForEffect(choices, sa, title, null);
|
||||
if (c == null) {
|
||||
return;
|
||||
}
|
||||
attachments = new CardCollection(c);
|
||||
} else if (sa.hasParam("Object")) {
|
||||
attachments = AbilityUtils.getDefinedCards(source, sa.getParam("Object"), sa);
|
||||
} else {
|
||||
attachments = new CardCollection(source);
|
||||
}
|
||||
@@ -185,7 +193,8 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
players.add(player);
|
||||
}
|
||||
}
|
||||
final Player pa = p.getController().chooseSingleEntityForEffect(players, aura, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", CardTranslation.getTranslatedName(source.getName())));
|
||||
final Player pa = p.getController().chooseSingleEntityForEffect(players, aura,
|
||||
Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
|
||||
if (pa != null) {
|
||||
handleAura(source, pa);
|
||||
return true;
|
||||
@@ -198,7 +207,8 @@ public class AttachEffect extends SpellAbilityEffect {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Card o = p.getController().chooseSingleEntityForEffect(list, aura, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(source.getName())));
|
||||
final Card o = p.getController().chooseSingleEntityForEffect(list, aura,
|
||||
Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
|
||||
if (o != null) {
|
||||
handleAura(source, o);
|
||||
//source.enchantEntity((Card) o);
|
||||
|
||||
@@ -31,7 +31,7 @@ public class BondEffect extends SpellAbilityEffect {
|
||||
Card partner = cards.getFirst();
|
||||
// skip choice if only one card on list
|
||||
if (cards.size() > 1) {
|
||||
partner = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(cards, sa, Localizer.getInstance().getMessage("lblSelectACardPair"));
|
||||
partner = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(cards, sa, Localizer.getInstance().getMessage("lblSelectACardPair"), null);
|
||||
}
|
||||
|
||||
// pair choices together
|
||||
|
||||
@@ -18,7 +18,10 @@ import forge.util.CardTranslation;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChangeCombatantsEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -45,8 +48,12 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect {
|
||||
final Combat combat = game.getCombat();
|
||||
final GameEntity originalDefender = combat.getDefenderByAttacker(c);
|
||||
final FCollectionView<GameEntity> defs = combat.getDefenders();
|
||||
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), false);
|
||||
|
||||
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attacker", c);
|
||||
|
||||
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa, title, false, params);
|
||||
if (originalDefender != null && !originalDefender.equals(defender)) {
|
||||
AttackingBand ab = combat.getBandOfAttacker(c);
|
||||
if (ab != null) {
|
||||
|
||||
@@ -507,7 +507,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachedTo"), tgtC.getController(), tgtC);
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", tgtC.toString()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attach", tgtC);
|
||||
Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", tgtC.toString()), params);
|
||||
tgtC.attachToEntity(attachedTo);
|
||||
} else { // When it should enter the battlefield attached to an illegal permanent it fails
|
||||
continue;
|
||||
@@ -517,7 +519,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("AttachedToPlayer")) {
|
||||
FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("AttachedToPlayer"), sa);
|
||||
if (!list.isEmpty()) {
|
||||
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", tgtC.toString()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attach", tgtC);
|
||||
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", tgtC.toString()), params);
|
||||
tgtC.attachToEntity(attachedTo);
|
||||
}
|
||||
else { // When it should enter the battlefield attached to an illegal player it fails
|
||||
@@ -578,7 +582,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
defender = player.getController().chooseSingleEntityForEffect(e, sa, Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(movedCard.getName())));
|
||||
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(movedCard.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attacker", movedCard);
|
||||
defender = player.getController().chooseSingleEntityForEffect(e, sa, title, params);
|
||||
}
|
||||
if (defender != null) {
|
||||
combat.addAttacker(movedCard, defender);
|
||||
@@ -1039,7 +1046,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (!list.isEmpty()) {
|
||||
Card attachedTo = null;
|
||||
if (list.size() > 1) {
|
||||
attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName())));
|
||||
String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attach", c);
|
||||
attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, title, params);
|
||||
}
|
||||
else {
|
||||
attachedTo = list.get(0);
|
||||
@@ -1057,7 +1067,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("AttachedToPlayer")) {
|
||||
FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(source, sa.getParam("AttachedToPlayer"), sa);
|
||||
if (!list.isEmpty()) {
|
||||
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName())));
|
||||
String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attach", c);
|
||||
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, title, params);
|
||||
c.attachToEntity(attachedTo);
|
||||
}
|
||||
else { // When it should enter the battlefield attached to an illegal permanent it fails
|
||||
@@ -1080,7 +1093,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
defender = player.getController().chooseSingleEntityForEffect(e, sa, Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())));
|
||||
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attacker", c);
|
||||
defender = player.getController().chooseSingleEntityForEffect(e, sa, title, params);
|
||||
}
|
||||
if (defender != null) {
|
||||
combat.addAttacker(c, defender);
|
||||
|
||||
@@ -189,7 +189,7 @@ public class CharmEffect extends SpellAbilityEffect {
|
||||
|
||||
//String choosers = sa.getParam("Chooser");
|
||||
FCollection<Player> opponents = activator.getOpponents(); // all cards have Choser$ Opponent, so it's hardcoded here
|
||||
chooser = activator.getController().chooseSingleEntityForEffect(opponents, sa, "Choose an opponent");
|
||||
chooser = activator.getController().chooseSingleEntityForEffect(opponents, sa, "Choose an opponent", null);
|
||||
source.setChosenPlayer(chooser);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
|
||||
final CardCollectionView cl = CardLists.getType(land, type);
|
||||
if (!cl.isEmpty()) {
|
||||
final String prompt = Localizer.getInstance().getMessage("lblChoose") + " " + Lang.nounWithAmount(1, type);
|
||||
Card c = p.getController().chooseSingleEntityForEffect(cl, sa, prompt, false);
|
||||
Card c = p.getController().chooseSingleEntityForEffect(cl, sa, prompt, false, null);
|
||||
if (c != null) {
|
||||
chosen.add(c);
|
||||
}
|
||||
@@ -100,7 +100,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
|
||||
while (!creature.isEmpty()) {
|
||||
Card c = p.getController().chooseSingleEntityForEffect(creature, sa,
|
||||
Localizer.getInstance().getMessage("lblSelectCreatureWithTotalPowerLessOrEqualTo", (totP - chosenP - negativeNum))
|
||||
+ "\r\n(" + Localizer.getInstance().getMessage("lblSelected") + ":" + chosenPool + ")\r\n(" + Localizer.getInstance().getMessage("lblTotalPowerNum", chosenP) + ")", chosenP <= totP);
|
||||
+ "\r\n(" + Localizer.getInstance().getMessage("lblSelected") + ":" + chosenPool + ")\r\n(" + Localizer.getInstance().getMessage("lblTotalPowerNum", chosenP) + ")", chosenP <= totP, null);
|
||||
if (c == null) {
|
||||
if (p.getController().confirmAction(sa, PlayerActionConfirmMode.OptionalChoose, Localizer.getInstance().getMessage("lblCancelChooseConfirm"))) {
|
||||
break;
|
||||
@@ -120,7 +120,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
|
||||
Aggregates.random(choices, validAmount, chosen);
|
||||
} else {
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " ";
|
||||
chosen.addAll(p.getController().chooseCardsForEffect(choices, sa, title, minAmount, validAmount, !sa.hasParam("Mandatory")));
|
||||
chosen.addAll(p.getController().chooseCardsForEffect(choices, sa, title, minAmount, validAmount, !sa.hasParam("Mandatory"), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ import forge.game.card.Card;
|
||||
import forge.game.event.GameEventCardModeChosen;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -33,6 +32,7 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
|
||||
|
||||
final List<SpellAbility> abilities = Lists.newArrayList(sa.getAdditionalAbilityList("Choices"));
|
||||
final SpellAbility fallback = sa.getAdditionalAbility("FallbackAbility");
|
||||
final int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("ChoiceAmount", "1"), sa);
|
||||
|
||||
final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
|
||||
|
||||
@@ -43,8 +43,7 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
|
||||
for (SpellAbility saChoice : abilities) {
|
||||
if (!saChoice.getRestrictions().checkOtherRestrictions(host, saChoice, sa.getActivatingPlayer()) ) {
|
||||
saToRemove.add(saChoice);
|
||||
} else if (saChoice.hasParam("UnlessCost") &&
|
||||
"Player.IsRemembered".equals(saChoice.getParam("Defined"))) {
|
||||
} else if (saChoice.hasParam("UnlessCost")) {
|
||||
String unlessCost = saChoice.getParam("UnlessCost");
|
||||
// Sac a permanent in presence of Sigarda, Host of Herons
|
||||
// TODO: generalize this by testing if the unless cost can be paid
|
||||
@@ -65,27 +64,26 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
|
||||
SpellAbility chosenSA = null;
|
||||
List<SpellAbility> chosenSAs = Lists.newArrayList();
|
||||
if (sa.hasParam("AtRandom")) {
|
||||
if (!abilities.isEmpty()) {
|
||||
chosenSA = abilities.get(MyRandom.getRandom().nextInt(abilities.size()));
|
||||
}
|
||||
Aggregates.random(abilities, amount, chosenSAs);
|
||||
} else {
|
||||
chosenSA = p.getController().chooseSingleSpellForEffect(abilities, sa, Localizer.getInstance().getMessage("lblChooseOne"),
|
||||
ImmutableMap.of());
|
||||
chosenSAs = p.getController().chooseSpellAbilitiesForEffect(abilities, sa, "Choose", amount, ImmutableMap.of());
|
||||
}
|
||||
|
||||
if (chosenSA != null) {
|
||||
String chosenValue = chosenSA.getDescription();
|
||||
if (sa.hasParam("ShowChoice")) {
|
||||
boolean dontNotifySelf = sa.getParam("ShowChoice").equals("ExceptSelf");
|
||||
p.getGame().getAction().nofityOfValue(sa, p, chosenValue, dontNotifySelf ? sa.getActivatingPlayer() : null);
|
||||
|
||||
if (!chosenSAs.isEmpty()) {
|
||||
for (SpellAbility chosenSA : chosenSAs) {
|
||||
String chosenValue = chosenSA.getDescription();
|
||||
if (sa.hasParam("ShowChoice")) {
|
||||
boolean dontNotifySelf = sa.getParam("ShowChoice").equals("ExceptSelf");
|
||||
p.getGame().getAction().nofityOfValue(sa, p, chosenValue, dontNotifySelf ? sa.getActivatingPlayer() : null);
|
||||
}
|
||||
if (sa.hasParam("SetChosenMode")) {
|
||||
sa.getHostCard().setChosenMode(chosenValue);
|
||||
}
|
||||
p.getGame().fireEvent(new GameEventCardModeChosen(p, host.getName(), chosenValue, sa.hasParam("ShowChoice")));
|
||||
AbilityUtils.resolve(chosenSA);
|
||||
}
|
||||
if (sa.hasParam("SetChosenMode")) {
|
||||
sa.getHostCard().setChosenMode(chosenValue);
|
||||
}
|
||||
p.getGame().fireEvent(new GameEventCardModeChosen(p, host.getName(), chosenValue, sa.hasParam("ShowChoice")));
|
||||
AbilityUtils.resolve(chosenSA);
|
||||
} else {
|
||||
// no choices are valid, e.g. maybe all Unless costs are unpayable
|
||||
if (fallback != null) {
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ChoosePlayerEffect extends SpellAbilityEffect {
|
||||
if (random) {
|
||||
chosen = choices.isEmpty() ? null : Aggregates.random(choices);
|
||||
} else {
|
||||
chosen = choices.isEmpty() ? null : p.getController().chooseSingleEntityForEffect(choices, sa, choiceDesc);
|
||||
chosen = choices.isEmpty() ? null : p.getController().chooseSingleEntityForEffect(choices, sa, choiceDesc, null);
|
||||
}
|
||||
if( null != chosen ) {
|
||||
card.setChosenPlayer(chosen);
|
||||
|
||||
@@ -138,7 +138,7 @@ public class ChooseSourceEffect extends SpellAbilityEffect {
|
||||
final String choiceTitle = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseSource") + " ";
|
||||
Card o = null;
|
||||
do {
|
||||
o = p.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, sa, choiceTitle);
|
||||
o = p.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, sa, choiceTitle, null);
|
||||
} while (o == null);
|
||||
chosen.add(o);
|
||||
sourcesToChooseFrom.remove(o);
|
||||
|
||||
@@ -75,7 +75,7 @@ public class ClashEffect extends SpellAbilityEffect {
|
||||
*/
|
||||
final Card source = sa.getHostCard();
|
||||
final Player player = source.getController();
|
||||
final Player opponent = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(player.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseOpponent")) ;
|
||||
final Player opponent = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(player.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseOpponent"), null);
|
||||
final ZoneType lib = ZoneType.Library;
|
||||
|
||||
if (sa.hasParam("RememberClasher")) {
|
||||
|
||||
@@ -81,7 +81,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
||||
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " ";
|
||||
cardToCopy = activator.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
||||
cardToCopy = activator.getController().chooseSingleEntityForEffect(choices, sa, title, false, null);
|
||||
} else if (sa.hasParam("Defined")) {
|
||||
List<Card> cloneSources = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
||||
if (!cloneSources.isEmpty()) {
|
||||
|
||||
@@ -36,9 +36,9 @@ public class ControlExchangeVariantEffect extends SpellAbilityEffect {
|
||||
CardCollectionView list2 = AbilityUtils.filterListByType(player2.getCardsIn(zone), type, sa);
|
||||
int max = Math.min(list1.size(), list2.size());
|
||||
// choose the same number of cards
|
||||
CardCollectionView chosen1 = activator.getController().chooseCardsForEffect(list1, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player1, 0, max, true);
|
||||
CardCollectionView chosen1 = activator.getController().chooseCardsForEffect(list1, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player1, 0, max, true, null);
|
||||
int num = chosen1.size();
|
||||
CardCollectionView chosen2 = activator.getController().chooseCardsForEffect(list2, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player2, num, num, true);
|
||||
CardCollectionView chosen2 = activator.getController().chooseCardsForEffect(list2, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player2, num, num, true, null);
|
||||
// check all cards can be controlled by the other player
|
||||
for (final Card c : chosen1) {
|
||||
if (!c.canBeControlledBy(player2)) {
|
||||
|
||||
@@ -2,8 +2,10 @@ package forge.game.ability.effects;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.game.Game;
|
||||
@@ -211,9 +213,10 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
||||
final Combat combat = game.getCombat();
|
||||
if ( null != combat ) {
|
||||
final FCollectionView<GameEntity> e = combat.getDefenders();
|
||||
|
||||
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(tgtC.getName())));
|
||||
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(tgtC.getName()));
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attacker", tgtC);
|
||||
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa, title, params);
|
||||
|
||||
if (defender != null) {
|
||||
combat.addAttacker(tgtC, defender);
|
||||
|
||||
@@ -143,7 +143,7 @@ public class CopyPermanentEffect extends TokenEffectBase {
|
||||
if (!choices.isEmpty()) {
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") +" ";
|
||||
|
||||
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
||||
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false, null);
|
||||
|
||||
if (choosen != null) {
|
||||
tgtCards.add(choosen);
|
||||
|
||||
@@ -146,7 +146,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
||||
valid.remove(originalTarget);
|
||||
mayChooseNewTargets = false;
|
||||
if (sa.hasParam("ChooseOnlyOne")) {
|
||||
Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseOne"));
|
||||
Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseOne"), null);
|
||||
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true);
|
||||
resetFirstTargetOnCopy(copy, choice, targetedSA);
|
||||
copies.add(copy);
|
||||
|
||||
@@ -7,6 +7,7 @@ import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
@@ -26,17 +27,30 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
Card source = null;
|
||||
List<Card> srcCards = getDefinedCardsOrTargeted(sa, "Source");
|
||||
|
||||
if (srcCards.size() > 0) {
|
||||
source = srcCards.get(0);
|
||||
}
|
||||
final List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
||||
|
||||
Card source = null;
|
||||
if (sa.usesTargeting() && sa.getTargetRestrictions().getMinTargets(host, sa) == 2) {
|
||||
if (tgtCards.size() < 2) {
|
||||
return "";
|
||||
}
|
||||
source = tgtCards.remove(0);
|
||||
} else {
|
||||
List<Card> srcCards = getDefinedCardsOrTargeted(sa, "Source");
|
||||
|
||||
if (srcCards.size() > 0) {
|
||||
source = srcCards.get(0);
|
||||
}
|
||||
}
|
||||
final String countername = sa.getParam("CounterType");
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
|
||||
final String counterAmount = sa.getParam("CounterNum");
|
||||
int amount = 0;
|
||||
if (!"Any".equals(counterAmount) && !"All".equals(counterAmount)) {
|
||||
amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
|
||||
}
|
||||
|
||||
sb.append("Move ");
|
||||
if ("Any".matches(countername)) {
|
||||
@@ -45,16 +59,18 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
} else {
|
||||
sb.append(amount).append(" ").append(" counter");
|
||||
}
|
||||
} else {
|
||||
} else if ("All".equals(countername)) {
|
||||
sb.append("all counter");
|
||||
} else {
|
||||
sb.append(amount).append(" ").append(countername).append(" counter");
|
||||
}
|
||||
if (amount != 1) {
|
||||
sb.append("s");
|
||||
}
|
||||
sb.append(" from ").append(source).append(" to ");
|
||||
try{
|
||||
try {
|
||||
sb.append(tgtCards.get(0));
|
||||
} catch(final IndexOutOfBoundsException exception) {
|
||||
} catch (final IndexOutOfBoundsException exception) {
|
||||
System.out.println(TextUtil.concatWithSpace("Somehow this is missing targets?", source.toString()));
|
||||
}
|
||||
|
||||
@@ -70,12 +86,12 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
final Player player = sa.getActivatingPlayer();
|
||||
final PlayerController pc = player.getController();
|
||||
final Game game = host.getGame();
|
||||
|
||||
|
||||
CounterType cType = null;
|
||||
try {
|
||||
cType = AbilityUtils.getCounterType(counterName, sa);
|
||||
} catch (Exception e) {
|
||||
if (!counterName.matches("Any")) {
|
||||
if (!counterName.matches("Any") && !counterName.matches("All")) {
|
||||
try {
|
||||
cType = AbilityUtils.getCounterType(counterName, sa);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Counter type doesn't match, nor does an SVar exist with the type name.");
|
||||
return;
|
||||
}
|
||||
@@ -89,17 +105,12 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
CardCollectionView srcCards = game.getCardsIn(ZoneType.Battlefield);
|
||||
srcCards = CardLists.getValidCards(srcCards, sa.getParam("ValidSource"), player, host, sa);
|
||||
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
||||
|
||||
|
||||
if (tgtCards.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Card dest = tgtCards.get(0);
|
||||
|
||||
// target cant receive this counter type
|
||||
if (!dest.canReceiveCounters(cType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Card cur = game.getCardState(dest, null);
|
||||
if (cur == null || !cur.equalsWithTimestamp(dest)) {
|
||||
// Test to see if the card we're trying to add is in the expected state
|
||||
@@ -107,48 +118,57 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
}
|
||||
dest = cur;
|
||||
|
||||
int csum = 0;
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Target", dest);
|
||||
|
||||
// only select cards if the counterNum is any
|
||||
if (counterNum.equals("Any")) {
|
||||
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, Localizer.getInstance().getMessage("lblChooseTakeCountersCard", cType.getName()), 0, srcCards.size(), true);
|
||||
if ("All".equals(counterName)) {
|
||||
// only select cards if the counterNum is any
|
||||
if (counterNum.equals("Any")) {
|
||||
srcCards = CardLists.filter(srcCards, CardPredicates.hasCounters());
|
||||
srcCards = player.getController().chooseCardsForEffect(srcCards, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseTakeCountersCard", "any"), 0,
|
||||
srcCards.size(), true, params);
|
||||
}
|
||||
} else {
|
||||
// target cant receive this counter type
|
||||
if (!dest.canReceiveCounters(cType)) {
|
||||
return;
|
||||
}
|
||||
srcCards = CardLists.filter(srcCards, CardPredicates.hasCounter(cType));
|
||||
|
||||
// only select cards if the counterNum is any
|
||||
if (counterNum.equals("Any")) {
|
||||
params.put("CounterType", cType);
|
||||
srcCards = player.getController().chooseCardsForEffect(srcCards, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseTakeCountersCard", cType.getName()), 0,
|
||||
srcCards.size(), true, params);
|
||||
}
|
||||
}
|
||||
|
||||
Map<CounterType, Integer> countersToAdd = Maps.newHashMap();
|
||||
|
||||
for (Card src : srcCards) {
|
||||
// rule 121.5: If the first and second objects are the same object, nothing happens
|
||||
// rule 121.5: If the first and second objects are the same object, nothing
|
||||
// happens
|
||||
if (src.equals(dest)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int cmax = src.getCounters(cType);
|
||||
if (cmax <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int cnum = 0;
|
||||
if (counterNum.equals("All")) {
|
||||
cnum = cmax;
|
||||
} else if (counterNum.equals("Any")) {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", cType);
|
||||
params.put("Source", src);
|
||||
params.put("Target", dest);
|
||||
cnum = player.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounterFromCard", cType.getName(), CardTranslation.getTranslatedName(src.getName())), 0, cmax, params);
|
||||
if ("All".equals(counterName)) {
|
||||
final Map<CounterType, Integer> tgtCounters = Maps.newHashMap(src.getCounters());
|
||||
for (Map.Entry<CounterType, Integer> e : tgtCounters.entrySet()) {
|
||||
removeCounter(sa, src, dest, e.getKey(), counterNum, countersToAdd);
|
||||
}
|
||||
} else {
|
||||
cnum = AbilityUtils.calculateAmount(host, counterNum, sa);
|
||||
}
|
||||
if(cnum > 0) {
|
||||
src.subtractCounter(cType, cnum);
|
||||
game.updateLastStateForCard(src);
|
||||
csum += cnum;
|
||||
removeCounter(sa, src, dest, cType, counterNum, countersToAdd);
|
||||
}
|
||||
}
|
||||
for (Map.Entry<CounterType, Integer> e : countersToAdd.entrySet()) {
|
||||
dest.addCounter(e.getKey(), e.getValue(), player, true, table);
|
||||
}
|
||||
|
||||
if (csum > 0) {
|
||||
dest.addCounter(cType, csum, player, true, table);
|
||||
game.updateLastStateForCard(dest);
|
||||
table.triggerCountersPutAll(game);
|
||||
}
|
||||
game.updateLastStateForCard(dest);
|
||||
table.triggerCountersPutAll(game);
|
||||
return;
|
||||
} else if (sa.hasParam("ValidDefined")) {
|
||||
// one Source to many Targets
|
||||
@@ -163,18 +183,25 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
if (source.getCounters(cType) <= 0) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", cType);
|
||||
params.put("Source", source);
|
||||
|
||||
CardCollectionView tgtCards = game.getCardsIn(ZoneType.Battlefield);
|
||||
tgtCards = CardLists.getValidCards(tgtCards, sa.getParam("ValidDefined"), player, host, sa);
|
||||
|
||||
if (counterNum.equals("Any")) {
|
||||
tgtCards = player.getController().chooseCardsForEffect(tgtCards, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseCardToGetCountersFrom", cType.getName(), CardTranslation.getTranslatedName(source.getName())), 0, tgtCards.size(), true);
|
||||
tgtCards = player.getController().chooseCardsForEffect(
|
||||
tgtCards, sa, Localizer.getInstance().getMessage("lblChooseCardToGetCountersFrom",
|
||||
cType.getName(), CardTranslation.getTranslatedName(source.getName())),
|
||||
0, tgtCards.size(), true, params);
|
||||
}
|
||||
|
||||
boolean updateSource = false;
|
||||
|
||||
for (final Card dest : tgtCards) {
|
||||
// rule 121.5: If the first and second objects are the same object, nothing happens
|
||||
// rule 121.5: If the first and second objects are the same object, nothing
|
||||
// happens
|
||||
if (source.equals(dest)) {
|
||||
continue;
|
||||
}
|
||||
@@ -188,11 +215,14 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params = Maps.newHashMap();
|
||||
params.put("CounterType", cType);
|
||||
params.put("Source", source);
|
||||
params.put("Target", cur);
|
||||
int cnum = player.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblPutHowManyTargetCounterOnCard", cType.getName(), CardTranslation.getTranslatedName(cur.getName())), 0, source.getCounters(cType), params);
|
||||
int cnum = player.getController().chooseNumber(sa,
|
||||
Localizer.getInstance().getMessage("lblPutHowManyTargetCounterOnCard", cType.getName(),
|
||||
CardTranslation.getTranslatedName(cur.getName())),
|
||||
0, source.getCounters(cType), params);
|
||||
|
||||
if (cnum > 0) {
|
||||
source.subtractCounter(cType, cnum);
|
||||
@@ -207,96 +237,133 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
||||
table.triggerCountersPutAll(game);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Card source = null;
|
||||
int cntToMove = 0;
|
||||
List<Card> srcCards = getDefinedCardsOrTargeted(sa, "Source");
|
||||
if (srcCards.size() > 0) {
|
||||
source = srcCards.get(0);
|
||||
}
|
||||
|
||||
// source doesn't has any counters to move
|
||||
if (!source.hasCounters()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!counterNum.equals("All") && !counterNum.equals("Any")) {
|
||||
cntToMove = AbilityUtils.calculateAmount(host, counterNum, sa);
|
||||
} else {
|
||||
cntToMove = source.getCounters(cType);
|
||||
}
|
||||
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
||||
|
||||
for (final Card dest : tgtCards) {
|
||||
if (null != source && null != dest) {
|
||||
// rule 121.5: If the first and second objects are the same object, nothing happens
|
||||
if (source.equals(dest)) {
|
||||
continue;
|
||||
Card source = null;
|
||||
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
||||
// special logic for moving from Target to Target
|
||||
if (sa.usesTargeting() && sa.getTargetRestrictions().getMinTargets(host, sa) == 2) {
|
||||
if (tgtCards.size() < 2) {
|
||||
return;
|
||||
}
|
||||
Card cur = game.getCardState(dest, null);
|
||||
if (cur == null || !cur.equalsWithTimestamp(dest)) {
|
||||
// Test to see if the card we're trying to add is in the expected state
|
||||
continue;
|
||||
source = tgtCards.remove(0);
|
||||
} else {
|
||||
List<Card> srcCards = getDefinedCardsOrTargeted(sa, "Source");
|
||||
if (srcCards.size() > 0) {
|
||||
source = srcCards.get(0);
|
||||
}
|
||||
}
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!"Any".matches(counterName)) {
|
||||
if (!cur.canReceiveCounters(cType)) {
|
||||
// source doesn't has any counters to move
|
||||
if (!source.hasCounters()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final Card dest : tgtCards) {
|
||||
if (null != source && null != dest) {
|
||||
// rule 121.5: If the first and second objects are the same object, nothing
|
||||
// happens
|
||||
if (source.equals(dest)) {
|
||||
continue;
|
||||
}
|
||||
Card cur = game.getCardState(dest, null);
|
||||
if (cur == null || !cur.equalsWithTimestamp(dest)) {
|
||||
// Test to see if the card we're trying to add is in the expected state
|
||||
continue;
|
||||
}
|
||||
|
||||
if (counterNum.equals("Any")) {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", cType);
|
||||
params.put("Source", source);
|
||||
params.put("Target", cur);
|
||||
cntToMove = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounterFromCard", cType.getName(), CardTranslation.getTranslatedName(source.getName())), 0, cntToMove, params);
|
||||
}
|
||||
|
||||
if (source.getCounters(cType) >= cntToMove) {
|
||||
source.subtractCounter(cType, cntToMove);
|
||||
cur.addCounter(cType, cntToMove, player, true, table);
|
||||
game.updateLastStateForCard(cur);
|
||||
}
|
||||
} else {
|
||||
// any counterType currently only Leech Bonder
|
||||
final Map<CounterType, Integer> tgtCounters = source.getCounters();
|
||||
|
||||
final List<CounterType> typeChoices = Lists.newArrayList();
|
||||
// get types of counters
|
||||
for (CounterType ct : tgtCounters.keySet()) {
|
||||
if (dest.canReceiveCounters(ct)) {
|
||||
typeChoices.add(ct);
|
||||
Map<CounterType, Integer> countersToAdd = Maps.newHashMap();
|
||||
if ("All".equals(counterName)) {
|
||||
final Map<CounterType, Integer> tgtCounters = Maps.newHashMap(source.getCounters());
|
||||
for (Map.Entry<CounterType, Integer> e : tgtCounters.entrySet()) {
|
||||
removeCounter(sa, source, cur, e.getKey(), counterNum, countersToAdd);
|
||||
}
|
||||
}
|
||||
if (typeChoices.isEmpty()) {
|
||||
return;
|
||||
|
||||
} else if ("Any".equals(counterName)) {
|
||||
// any counterType currently only Leech Bonder
|
||||
final Map<CounterType, Integer> tgtCounters = source.getCounters();
|
||||
|
||||
final List<CounterType> typeChoices = Lists.newArrayList();
|
||||
// get types of counters
|
||||
for (CounterType ct : tgtCounters.keySet()) {
|
||||
if (dest.canReceiveCounters(ct)) {
|
||||
typeChoices.add(ct);
|
||||
}
|
||||
}
|
||||
if (typeChoices.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Source", source);
|
||||
params.put("Target", dest);
|
||||
String title = Localizer.getInstance().getMessage("lblSelectRemoveCounterType");
|
||||
CounterType chosenType = pc.chooseCounterType(typeChoices, sa, title, params);
|
||||
|
||||
removeCounter(sa, source, cur, chosenType, counterNum, countersToAdd);
|
||||
} else {
|
||||
if (!cur.canReceiveCounters(cType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
removeCounter(sa, source, cur, cType, counterNum, countersToAdd);
|
||||
}
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Source", source);
|
||||
params.put("Target", dest);
|
||||
String title = Localizer.getInstance().getMessage("lblSelectRemoveCounterType");
|
||||
CounterType chosenType = pc.chooseCounterType(typeChoices, sa, title, params);
|
||||
|
||||
params = Maps.newHashMap();
|
||||
params.put("CounterType", chosenType);
|
||||
params.put("Source", source);
|
||||
params.put("Target", dest);
|
||||
int chosenAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounters", chosenType.getName()),
|
||||
0, Math.min(tgtCounters.get(chosenType), cntToMove), params);
|
||||
|
||||
if (chosenAmount > 0) {
|
||||
dest.addCounter(chosenType, chosenAmount, player, true, table);
|
||||
source.subtractCounter(chosenType, chosenAmount);
|
||||
game.updateLastStateForCard(dest);
|
||||
cntToMove -= chosenAmount;
|
||||
for (Map.Entry<CounterType, Integer> e : countersToAdd.entrySet()) {
|
||||
cur.addCounter(e.getKey(), e.getValue(), player, true, table);
|
||||
}
|
||||
game.updateLastStateForCard(cur);
|
||||
}
|
||||
}
|
||||
// update source
|
||||
game.updateLastStateForCard(source);
|
||||
}
|
||||
// update source
|
||||
game.updateLastStateForCard(source);
|
||||
table.triggerCountersPutAll(game);
|
||||
} // moveCounterResolve
|
||||
|
||||
protected void removeCounter(SpellAbility sa, final Card src, final Card dest, CounterType cType, String counterNum, Map<CounterType, Integer> countersToAdd) {
|
||||
final Card host = sa.getHostCard();
|
||||
//final String counterNum = sa.getParam("CounterNum");
|
||||
final Player player = sa.getActivatingPlayer();
|
||||
final PlayerController pc = player.getController();
|
||||
final Game game = host.getGame();
|
||||
|
||||
// rule 121.5: If the first and second objects are the same object, nothing
|
||||
// happens
|
||||
if (src.equals(dest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dest.canReceiveCounters(cType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int cmax = src.getCounters(cType);
|
||||
if (cmax <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int cnum = 0;
|
||||
if (counterNum.equals("All")) {
|
||||
cnum = cmax;
|
||||
} else if (counterNum.equals("Any")) {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", cType);
|
||||
params.put("Source", src);
|
||||
params.put("Target", dest);
|
||||
cnum = pc.chooseNumber(
|
||||
sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounterFromCard",
|
||||
cType.getName(), CardTranslation.getTranslatedName(src.getName())),
|
||||
0, cmax, params);
|
||||
} else {
|
||||
cnum = Math.min(cmax, AbilityUtils.calculateAmount(host, counterNum, sa));
|
||||
}
|
||||
if (cnum > 0) {
|
||||
src.subtractCounter(cType, cnum);
|
||||
game.updateLastStateForCard(src);
|
||||
countersToAdd.put(cType, (countersToAdd.containsKey(cType) ? countersToAdd.get(cType) : 0) + cnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
|
||||
list.addAll(CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.hasCounters()));
|
||||
|
||||
List<GameEntity> result = pc.chooseEntitiesForEffect(list, 0, list.size(), null, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseProliferateTarget"), p);
|
||||
Localizer.getInstance().getMessage("lblChooseProliferateTarget"), p, null);
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
for (final GameEntity ge : result) {
|
||||
|
||||
@@ -18,7 +18,7 @@ public class CountersPutAllEffect extends SpellAbilityEffect {
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
final CounterType cType = CounterType.valueOf(sa.getParam("CounterType"));
|
||||
final CounterType cType = CounterType.getType(sa.getParam("CounterType"));
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
|
||||
final String zone = sa.hasParam("ValidZone") ? sa.getParam("ValidZone") : "Battlefield";
|
||||
|
||||
@@ -67,7 +67,7 @@ public class CountersPutAllEffect extends SpellAbilityEffect {
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
for (final Card tgtCard : cards) {
|
||||
boolean inBattlefield = game.getZoneOf(tgtCard).is(ZoneType.Battlefield);
|
||||
tgtCard.addCounter(CounterType.valueOf(type), counterAmount, placer, inBattlefield, table);
|
||||
tgtCard.addCounter(CounterType.getType(type), counterAmount, placer, inBattlefield, table);
|
||||
game.updateLastStateForCard(tgtCard);
|
||||
}
|
||||
table.triggerCountersPutAll(game);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -8,20 +9,25 @@ import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameEntityCounterTable;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerHandler;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -31,6 +37,7 @@ import forge.util.CardTranslation;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -63,7 +70,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
} else if (type.equals("EachFromSource")) {
|
||||
stringBuilder.append("each counter");
|
||||
} else {
|
||||
stringBuilder.append(CounterType.valueOf(type).getName()).append(" counter");
|
||||
stringBuilder.append(CounterType.getType(type).getName()).append(" counter");
|
||||
}
|
||||
|
||||
if (amount != 1) {
|
||||
@@ -112,48 +119,27 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
protected void resolvePerType(SpellAbility sa, final Player placer, CounterType counterType, int counterAmount, GameEntityCounterTable table) {
|
||||
final Card card = sa.getHostCard();
|
||||
final Game game = card.getGame();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final PlayerController pc = activator.getController();
|
||||
|
||||
String strTyp = sa.getParam("CounterType");
|
||||
CounterType counterType = null;
|
||||
boolean existingCounter = strTyp.equals("ExistingCounter");
|
||||
boolean eachExistingCounter = sa.hasParam("EachExistingCounter");
|
||||
boolean eachFromSource = strTyp.equals("EachFromSource");
|
||||
String amount = sa.getParamOrDefault("CounterNum", "1");
|
||||
|
||||
if (!existingCounter && !eachFromSource) {
|
||||
try {
|
||||
counterType = AbilityUtils.getCounterType(strTyp, sa);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Counter type doesn't match, nor does an SVar exist with the type name.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Player placer = activator;
|
||||
if (sa.hasParam("Placer")) {
|
||||
final String pstr = sa.getParam("Placer");
|
||||
placer = AbilityUtils.getDefinedPlayers(sa.getHostCard(), pstr, sa).get(0);
|
||||
}
|
||||
|
||||
final boolean etbcounter = sa.hasParam("ETB");
|
||||
final boolean remember = sa.hasParam("RememberCounters");
|
||||
final boolean rememberCards = sa.hasParam("RememberCards");
|
||||
int counterAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amount, sa);
|
||||
final int max = sa.hasParam("MaxFromEffect") ? Integer.parseInt(sa.getParam("MaxFromEffect")) : -1;
|
||||
|
||||
CardCollection tgtCards = new CardCollection();
|
||||
boolean existingCounter = sa.hasParam("CounterType") && sa.getParam("CounterType").equals("ExistingCounter");
|
||||
boolean eachExistingCounter = sa.hasParam("EachExistingCounter");
|
||||
|
||||
List<GameObject> tgtObjects = Lists.newArrayList();
|
||||
if (sa.hasParam("Bolster")) {
|
||||
CardCollection creatsYouCtrl = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
|
||||
CardCollection leastToughness = new CardCollection(Aggregates.listWithMin(creatsYouCtrl, CardPredicates.Accessors.fnGetDefense));
|
||||
tgtCards.addAll(activator.getController().chooseCardsForEffect(leastToughness, sa, Localizer.getInstance().getMessage("lblChooseACreatureWithLeastToughness"), 1, 1, false));
|
||||
tgtObjects.addAll(tgtCards);
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", counterType);
|
||||
|
||||
Iterables.addAll(tgtObjects, activator.getController().chooseCardsForEffect(leastToughness, sa, Localizer.getInstance().getMessage("lblChooseACreatureWithLeastToughness"), 1, 1, false, params));
|
||||
} else if (sa.hasParam("Choices")) {
|
||||
ZoneType choiceZone = ZoneType.Battlefield;
|
||||
if (sa.hasParam("ChoiceZone")) {
|
||||
@@ -174,14 +160,22 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
|
||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, card, sa);
|
||||
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " ";
|
||||
tgtObjects.addAll(new CardCollection(chooser.getController().chooseCardsForEffect(choices, sa, title, n, n, sa.hasParam("ChoiceOptional"))));
|
||||
String title = Localizer.getInstance().getMessage("lblChooseaCard") + " ";
|
||||
if (sa.hasParam("ChoiceTitle")) {
|
||||
title = sa.getParam("ChoiceTitle");
|
||||
// TODO might use better message
|
||||
if (counterType != null) {
|
||||
title += " " + counterType.getName();
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", counterType);
|
||||
Iterables.addAll(tgtObjects, chooser.getController().chooseCardsForEffect(choices, sa, title, n, n, sa.hasParam("ChoiceOptional"), params));
|
||||
} else {
|
||||
tgtObjects.addAll(getDefinedOrTargeted(sa, "Defined"));
|
||||
}
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
|
||||
for (final GameObject obj : tgtObjects) {
|
||||
// check if the object is still in game or if it was moved
|
||||
Card gameCard = null;
|
||||
@@ -234,9 +228,8 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
if (eachFromSource) {
|
||||
final CardCollection definedCard = AbilityUtils.getDefinedCards(card, sa.getParam("EachFromSource"), sa);
|
||||
for (Card c : definedCard) {
|
||||
if (sa.hasParam("EachFromSource")) {
|
||||
for (Card c : AbilityUtils.getDefinedCards(card, sa.getParam("EachFromSource"), sa)) {
|
||||
for (Entry<CounterType, Integer> cti : c.getCounters().entrySet()) {
|
||||
if (gameCard != null && gameCard.canReceiveCounters(cti.getKey())) {
|
||||
gameCard.addCounter(cti.getKey(), cti.getValue(), placer, true, table);
|
||||
@@ -262,7 +255,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
|
||||
// Adapt need extra logic
|
||||
if (sa.hasParam("Adapt")) {
|
||||
if (!(gameCard.getCounters(CounterType.P1P1) == 0
|
||||
if (!(gameCard.getCounters(CounterEnumType.P1P1) == 0
|
||||
|| gameCard.hasKeyword("CARDNAME adapts as though it had no +1/+1 counters"))) {
|
||||
continue;
|
||||
}
|
||||
@@ -293,8 +286,13 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", counterType);
|
||||
params.put("Amount", counterAmount);
|
||||
params.put("Target", gameCard);
|
||||
|
||||
String message = Localizer.getInstance().getMessage("lblDoYouWantPutTargetP1P1CountersOnCard", String.valueOf(counterAmount), CardTranslation.getTranslatedName(gameCard.getName()));
|
||||
Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseAnOpponent"));
|
||||
Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseAnOpponent"), params);
|
||||
|
||||
if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message)) {
|
||||
gameCard.setTributed(true);
|
||||
@@ -307,13 +305,14 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
if (etbcounter) {
|
||||
gameCard.addEtbCounter(counterType, counterAmount, placer);
|
||||
} else {
|
||||
if (gameCard.addCounter(counterType, counterAmount, placer, true, table) > 0) {
|
||||
int addedAmount = gameCard.addCounter(counterType, counterAmount, placer, true, table);
|
||||
if (addedAmount > 0) {
|
||||
counterAdded = true;
|
||||
}
|
||||
}
|
||||
if (remember) {
|
||||
final int value = gameCard.getTotalCountersToAdd();
|
||||
gameCard.addCountersAddedBy(card, counterType, value);
|
||||
|
||||
if (sa.hasParam("RemovePhase")) {
|
||||
addRemovePhaseTrigger(card, sa, sa.getParam("RemovePhase"), gameCard, counterType, addedAmount);
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.hasParam("Evolve")) {
|
||||
@@ -356,7 +355,85 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
||||
pl.addCounter(counterType, counterAmount, placer, true, table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card card = sa.getHostCard();
|
||||
final Game game = card.getGame();
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
|
||||
CounterType counterType = null;
|
||||
String amount = sa.getParamOrDefault("CounterNum", "1");
|
||||
boolean rememberAmount = sa.hasParam("RememberAmount");
|
||||
|
||||
if (!sa.hasParam("EachExistingCounter") && !sa.hasParam("EachFromSource") && !sa.hasParam("SharedKeywords")) {
|
||||
try {
|
||||
counterType = AbilityUtils.getCounterType(sa.getParam("CounterType"), sa);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Counter type doesn't match, nor does an SVar exist with the type name.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Player placer = activator;
|
||||
if (sa.hasParam("Placer")) {
|
||||
final String pstr = sa.getParam("Placer");
|
||||
placer = AbilityUtils.getDefinedPlayers(sa.getHostCard(), pstr, sa).get(0);
|
||||
}
|
||||
|
||||
int counterAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amount, sa);
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
|
||||
if (sa.hasParam("SharedKeywords")) {
|
||||
List<String> keywords = Arrays.asList(sa.getParam("SharedKeywords").split(" & "));
|
||||
List<ZoneType> zones = ZoneType.listValueOf(sa.getParam("SharedKeywordsZone"));
|
||||
String[] restrictions = sa.hasParam("SharedRestrictions") ? sa.getParam("SharedRestrictions").split(",") : new String[]{"Card"};
|
||||
keywords = CardFactoryUtil.sharedKeywords(keywords, restrictions, zones, sa.getHostCard());
|
||||
for (String k : keywords) {
|
||||
resolvePerType(sa, placer, CounterType.getType(k), counterAmount, table);
|
||||
}
|
||||
} else {
|
||||
resolvePerType(sa, placer, counterType, counterAmount, table);
|
||||
}
|
||||
|
||||
int totalAdded = 0;
|
||||
for (Integer i : table.values()) {
|
||||
totalAdded += i;
|
||||
}
|
||||
|
||||
if (totalAdded > 0 && rememberAmount) {
|
||||
// TODO use SpellAbility Remember later
|
||||
card.addRemembered(Integer.valueOf(totalAdded));
|
||||
}
|
||||
|
||||
table.triggerCountersPutAll(game);
|
||||
}
|
||||
|
||||
protected void addRemovePhaseTrigger(final Card host, final SpellAbility sa, String phase, Card tgt, CounterType ct, int added) {
|
||||
boolean intrinsic = sa.isIntrinsic();
|
||||
|
||||
StringBuilder delTrig = new StringBuilder("Mode$ Phase | Phase$ ");
|
||||
delTrig.append(phase);
|
||||
delTrig.append(" | TriggerDescription$ For each ").append(ct.getName()).append(" counter you put on a creature this way, remove a ").append(ct.getName()).append(" counter from that creature at the beginning of the next");
|
||||
if ("Cleanup".equals(phase)) {
|
||||
delTrig.append("cleanup step");
|
||||
} else if ("End of Turn".equals(phase)) {
|
||||
delTrig.append("next end step");
|
||||
}
|
||||
|
||||
String trigSA = new StringBuilder("DB$ RemoveCounter | Defined$ DelayTriggerRemembered | CounterNum$ 1 | CounterType$ ").append(ct).toString();
|
||||
|
||||
// these trigger are one per counter
|
||||
for (int i = 0; i < added; i++) {
|
||||
final Trigger trig = TriggerHandler.parseTrigger(delTrig.toString(), sa.getHostCard(), intrinsic);
|
||||
trig.addRemembered(tgt);
|
||||
|
||||
final SpellAbility newSa = AbilityFactory.getAbility(trigSA, sa.getHostCard());
|
||||
newSa.setIntrinsic(intrinsic);
|
||||
trig.setOverridingAbility(newSa);
|
||||
sa.getActivatingPlayer().getGame().getTriggerHandler().registerDelayedTrigger(trig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
||||
sb.append(sa.getActivatingPlayer().getName());
|
||||
|
||||
if (sa.hasParam("CounterType")) {
|
||||
CounterType ctype = CounterType.valueOf(sa.getParam("CounterType"));
|
||||
CounterType ctype = CounterType.getType(sa.getParam("CounterType"));
|
||||
sb.append(" removes a ").append(ctype.getName());
|
||||
sb.append(" counter from or put another ").append(ctype.getName()).append(" counter on ");
|
||||
} else {
|
||||
@@ -56,7 +56,7 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
||||
|
||||
CounterType ctype = null;
|
||||
if (sa.hasParam("CounterType")) {
|
||||
ctype = CounterType.valueOf(sa.getParam("CounterType"));
|
||||
ctype = CounterType.getType(sa.getParam("CounterType"));
|
||||
}
|
||||
|
||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||
|
||||
@@ -18,7 +18,7 @@ public class CountersRemoveAllEffect extends SpellAbilityEffect {
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
final CounterType cType = CounterType.valueOf(sa.getParam("CounterType"));
|
||||
final CounterType cType = CounterType.getType(sa.getParam("CounterType"));
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
|
||||
final String zone = sa.hasParam("ValidZone") ? sa.getParam("ValidZone") : "Battlefield";
|
||||
String amountString = Integer.toString(amount);
|
||||
@@ -69,11 +69,11 @@ public class CountersRemoveAllEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
}
|
||||
if (sa.hasParam("AllCounters")) {
|
||||
counterAmount = tgtCard.getCounters(CounterType.valueOf(type));
|
||||
counterAmount = tgtCard.getCounters(CounterType.getType(type));
|
||||
}
|
||||
|
||||
if (counterAmount > 0) {
|
||||
tgtCard.subtractCounter(CounterType.valueOf(type), counterAmount);
|
||||
tgtCard.subtractCounter(CounterType.getType(type), counterAmount);
|
||||
game.updateLastStateForCard(tgtCard);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
final String num = sa.getParam("CounterNum");
|
||||
|
||||
int amount = 0;
|
||||
if (!num.equals("All") && !num.equals("Remembered")) {
|
||||
if (!num.equals("All") && !num.equals("Any")) {
|
||||
amount = AbilityUtils.calculateAmount(sa.getHostCard(), num, sa);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
sb.append(amount).append(" ").append(" counter");
|
||||
}
|
||||
} else {
|
||||
sb.append(amount).append(" ").append(CounterType.valueOf(counterName).getName()).append(" counter");
|
||||
sb.append(amount).append(" ").append(CounterType.getType(counterName).getName()).append(" counter");
|
||||
}
|
||||
if (amount != 1) {
|
||||
sb.append("s");
|
||||
@@ -80,7 +80,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
final String num = sa.getParam("CounterNum");
|
||||
|
||||
int cntToRemove = 0;
|
||||
if (!num.equals("All") && !num.equals("Any") && !num.equals("Remembered")) {
|
||||
if (!num.equals("All") && !num.equals("Any")) {
|
||||
cntToRemove = AbilityUtils.calculateAmount(sa.getHostCard(), num, sa);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,10 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
srcCards = game.getCardsIn(ZoneType.Battlefield);
|
||||
srcCards = CardLists.getValidCards(srcCards, sa.getParam("ValidSource"), player, card, sa);
|
||||
if (num.equals("Any")) {
|
||||
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, Localizer.getInstance().getMessage("lblChooseCardsToTakeTargetCounters", counterType.getName()), 0, srcCards.size(), true);
|
||||
String title = Localizer.getInstance().getMessage("lblChooseCardsToTakeTargetCounters", counterType.getName());
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("CounterType", counterType);
|
||||
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, title, 0, srcCards.size(), true, params);
|
||||
}
|
||||
} else {
|
||||
srcCards = getTargetCards(sa);
|
||||
@@ -156,8 +159,6 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
continue;
|
||||
} else if (num.equals("All") || num.equals("Any")) {
|
||||
cntToRemove = gameCard.getCounters(counterType);
|
||||
} else if (num.equals("Remembered")) {
|
||||
cntToRemove = gameCard.getCountersAddedBy(card, counterType);
|
||||
}
|
||||
|
||||
if (type.equals("Any")) {
|
||||
@@ -169,7 +170,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("UpTo") || num.equals("Any")) {
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Target", gameCard);
|
||||
params.put("CounterType", type);
|
||||
params.put("CounterType", counterType);
|
||||
String title = Localizer.getInstance().getMessage("lblSelectRemoveCountersNumberOfTarget", type);
|
||||
cntToRemove = pc.chooseNumber(sa, title, 0, cntToRemove, params);
|
||||
}
|
||||
@@ -179,6 +180,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
||||
gameCard.subtractCounter(counterType, cntToRemove);
|
||||
if (rememberRemoved) {
|
||||
for (int i = 0; i < cntToRemove; i++) {
|
||||
// TODO might need to be more specific
|
||||
card.addRemembered(Pair.of(counterType, i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("Choser")) {
|
||||
final FCollectionView<Player> choosers = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Choser"), sa);
|
||||
if (!choosers.isEmpty()) {
|
||||
chooser = player.getController().chooseSingleEntityForEffect(choosers, sa, Localizer.getInstance().getMessage("lblChooser") + ":");
|
||||
chooser = player.getController().chooseSingleEntityForEffect(choosers, null, sa, Localizer.getInstance().getMessage("lblChooser") + ":", false, p, null);
|
||||
}
|
||||
if (sa.hasParam("SetChosenPlayer")) {
|
||||
host.setChosenPlayer(chooser);
|
||||
@@ -221,7 +221,7 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
}
|
||||
for (final byte pair : MagicColor.COLORPAIR) {
|
||||
Card chosen = chooser.getController().chooseSingleEntityForEffect(CardLists.filter(valid, CardPredicates.isExactlyColor(pair)),
|
||||
delayedReveal, sa, Localizer.getInstance().getMessage("lblChooseOne"), false, p);
|
||||
delayedReveal, sa, Localizer.getInstance().getMessage("lblChooseOne"), false, p, null);
|
||||
if (chosen != null) {
|
||||
movedCards.add(chosen);
|
||||
}
|
||||
@@ -241,7 +241,7 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
prompt = Localizer.getInstance().getMessage("lblChooseACardLeaveTarget", p.getName(), destZone2.getTranslatedName());
|
||||
}
|
||||
|
||||
Card chosen = chooser.getController().chooseSingleEntityForEffect(valid, delayedReveal, sa, prompt, false, p);
|
||||
Card chosen = chooser.getController().chooseSingleEntityForEffect(valid, delayedReveal, sa, prompt, false, p, null);
|
||||
movedCards.remove(chosen);
|
||||
if (sa.hasParam("RandomOrder")) {
|
||||
CardLists.shuffle(movedCards);
|
||||
@@ -274,7 +274,7 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
int max = anyNumber ? valid.size() : Math.min(valid.size(),destZone1ChangeNum);
|
||||
int min = (anyNumber || optional) ? 0 : max;
|
||||
if ( max > 0 ) { // if max is 0 don't make a choice
|
||||
chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p);
|
||||
chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p, null);
|
||||
}
|
||||
|
||||
chooser.getController().endTempShowCards();
|
||||
@@ -316,7 +316,7 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
final FCollectionView<GameEntity> e = combat.getDefenders();
|
||||
|
||||
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())));
|
||||
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), null);
|
||||
|
||||
if (defender != null) {
|
||||
combat.addAttacker(c, defender);
|
||||
|
||||
@@ -90,7 +90,7 @@ public class DigMultipleEffect extends SpellAbilityEffect {
|
||||
int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("ChooseAmount", "1"), sa);
|
||||
final ZoneType chosenZone = sa.hasParam("ChosenZone") ? ZoneType.smartValueOf(sa.getParam("ChosenZone")) : ZoneType.Battlefield;
|
||||
|
||||
CardCollectionView extraChosen = chooser.getController().chooseCardsForEffect(chosen, sa, Localizer.getInstance().getMessage("lblChooseCards"), amount, amount, false);
|
||||
CardCollectionView extraChosen = chooser.getController().chooseCardsForEffect(chosen, sa, Localizer.getInstance().getMessage("lblChooseCards"), amount, amount, false, null);
|
||||
if (!extraChosen.isEmpty()) {
|
||||
game.getAction().reveal(extraChosen, chooser, true, Localizer.getInstance().getMessage("lblPlayerPickedCardFrom", chooser.getName()));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import forge.util.collect.FCollectionView;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
public class DigUntilEffect extends SpellAbilityEffect {
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -153,7 +155,7 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
if (revealed.size() > 0) {
|
||||
game.getAction().reveal(revealed, p, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (foundDest != null) {
|
||||
// Allow ordering of found cards
|
||||
@@ -178,11 +180,14 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
||||
}
|
||||
if (sa.hasParam("Attacking")) {
|
||||
final Combat combat = game.getCombat();
|
||||
if ( null != combat ) {
|
||||
if (null != combat) {
|
||||
final FCollectionView<GameEntity> e = combat.getDefenders();
|
||||
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attacker", c);
|
||||
|
||||
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa,
|
||||
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())));
|
||||
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), params);
|
||||
|
||||
if (defender != null) {
|
||||
combat.addAttacker(c, defender);
|
||||
|
||||
@@ -86,7 +86,7 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (sa.hasParam("ForgetCounter")) {
|
||||
CounterType cType = CounterType.valueOf(sa.getParam("ForgetCounter"));
|
||||
CounterType cType = CounterType.getType(sa.getParam("ForgetCounter"));
|
||||
rememberList = new FCollection<GameObject>(CardLists.filter(Iterables.filter(rememberList, Card.class), CardPredicates.hasCounter(cType)));
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class EncodeEffect extends SpellAbilityEffect {
|
||||
Card movedCard = game.getAction().moveTo(ZoneType.Exile, host, sa);
|
||||
|
||||
// choose a creature
|
||||
Card choice = player.getController().chooseSingleEntityForEffect(choices, sa, Localizer.getInstance().getMessage("lblChooseACreatureYouControlToEncode") + " ", true);
|
||||
Card choice = player.getController().chooseSingleEntityForEffect(choices, sa, Localizer.getInstance().getMessage("lblChooseACreatureYouControlToEncode") + " ", true, null);
|
||||
|
||||
if (choice == null) {
|
||||
return;
|
||||
|
||||
@@ -7,7 +7,7 @@ import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -78,7 +78,7 @@ public class ExploreEffect extends SpellAbilityEffect {
|
||||
// if the card is not more in the game anymore
|
||||
// this might still return true but its no problem
|
||||
if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.equalsWithTimestamp(c)) {
|
||||
c.addCounter(CounterType.P1P1, 1, pl, true, table);
|
||||
c.addCounter(CounterEnumType.P1P1, 1, pl, true, table);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ public class ManifestEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseCardToManifest") + " ";
|
||||
tgtCards = new CardCollection(activator.getController().chooseEntitiesForEffect(choices, amount, amount, null, sa, title, p));
|
||||
|
||||
tgtCards = new CardCollection(activator.getController().chooseCardsForEffect(choices, sa, title, amount, amount, false, null));
|
||||
} else if ("TopOfLibrary".equals(defined)) {
|
||||
tgtCards = p.getTopXCardsFromLibrary(amount);
|
||||
} else {
|
||||
|
||||
@@ -36,7 +36,7 @@ public class MeldEffect extends SpellAbilityEffect {
|
||||
return;
|
||||
}
|
||||
|
||||
Card secondary = controller.getController().chooseSingleEntityForEffect(field, sa, Localizer.getInstance().getMessage("lblChooseCardToMeld"));
|
||||
Card secondary = controller.getController().chooseSingleEntityForEffect(field, sa, Localizer.getInstance().getMessage("lblChooseCardToMeld"), null);
|
||||
|
||||
secondary = game.getAction().exile(secondary, sa);
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ public class MultiplePilesEffect extends SpellAbilityEffect {
|
||||
|
||||
for (int i = 1; i < piles; i++) {
|
||||
int size = pool.size();
|
||||
CardCollectionView pile = p.getController().chooseCardsForEffect(pool, sa, Localizer.getInstance().getMessage("lblChooseCardsInTargetPile", String.valueOf(i)), 0, size, false);
|
||||
CardCollectionView pile = p.getController().chooseCardsForEffect(pool, sa, Localizer.getInstance().getMessage("lblChooseCardsInTargetPile", String.valueOf(i)), 0, size, false, null);
|
||||
pileList.add(pile);
|
||||
pool.removeAll(pile);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
public class MustBlockEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -23,6 +25,13 @@ public class MustBlockEffect extends SpellAbilityEffect {
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final Game game = activator.getGame();
|
||||
|
||||
List<Card> cards;
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
|
||||
} else {
|
||||
cards = Lists.newArrayList(host);
|
||||
}
|
||||
|
||||
List<Card> tgtCards = Lists.newArrayList();
|
||||
if (sa.hasParam("Choices")) {
|
||||
Player chooser = activator;
|
||||
@@ -35,8 +44,9 @@ public class MustBlockEffect extends SpellAbilityEffect {
|
||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
||||
if (!choices.isEmpty()) {
|
||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") +" ";
|
||||
|
||||
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
||||
Map<String, Object> params = Maps.newHashMap();
|
||||
params.put("Attackers", cards);
|
||||
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false, params);
|
||||
|
||||
if (choosen != null) {
|
||||
tgtCards.add(choosen);
|
||||
@@ -48,13 +58,6 @@ public class MustBlockEffect extends SpellAbilityEffect {
|
||||
|
||||
final boolean mustBlockAll = sa.hasParam("BlockAllDefined");
|
||||
|
||||
List<Card> cards;
|
||||
if (sa.hasParam("DefinedAttacker")) {
|
||||
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
|
||||
} else {
|
||||
cards = Lists.newArrayList(host);
|
||||
}
|
||||
|
||||
for (final Card c : tgtCards) {
|
||||
if ((!sa.usesTargeting()) || c.canBeTargetedBy(sa)) {
|
||||
if (mustBlockAll) {
|
||||
|
||||
@@ -118,7 +118,7 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
final int choicenum = AbilityUtils.calculateAmount(source, sa.getParam("ChoiceNum"), sa);
|
||||
tgtCards = new CardCollection(
|
||||
activator.getController().chooseCardsForEffect(choice, sa,
|
||||
source + " - " + Localizer.getInstance().getMessage("lblChooseUpTo") + " " + Lang.nounWithNumeral(choicenum, "card"), 0, choicenum, true
|
||||
source + " - " + Localizer.getInstance().getMessage("lblChooseUpTo") + " " + Lang.nounWithNumeral(choicenum, "card"), 0, choicenum, true, null
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -145,7 +145,7 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
final CardCollection saidNoTo = new CardCollection();
|
||||
while (tgtCards.size() > saidNoTo.size() && saidNoTo.size() < amount && amount > 0) {
|
||||
activator.getController().tempShowCards(showCards);
|
||||
Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"));
|
||||
Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), null);
|
||||
activator.getController().endTempShowCards();
|
||||
if (tgtCard == null) {
|
||||
return;
|
||||
|
||||
@@ -5,7 +5,7 @@ import forge.game.GameEntityCounterTable;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.Lang;
|
||||
@@ -59,7 +59,7 @@ import java.util.List;
|
||||
sb.append("s");
|
||||
}
|
||||
|
||||
String type = CounterType.POISON.getName() + " counter";
|
||||
String type = CounterEnumType.POISON.getName() + " counter";
|
||||
|
||||
sb.append(" ").append(Lang.nounWithAmount(amount, type)).append(".");
|
||||
|
||||
|
||||
@@ -164,20 +164,6 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.hasParam("RepeatCounters")) {
|
||||
Card target = sa.getTargetCard();
|
||||
if (target == null) {
|
||||
target = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa).get(0);
|
||||
}
|
||||
for (CounterType type : target.getCounters().keySet()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Number$").append(target.getCounters(type));
|
||||
source.setSVar("RepeatSVarCounter", type.getName().toUpperCase());
|
||||
source.setSVar("RepeatCounterAmount", sb.toString());
|
||||
AbilityUtils.resolve(repeat);
|
||||
}
|
||||
}
|
||||
if (recordChoice) {
|
||||
boolean random = sa.hasParam("Random");
|
||||
Map<Player, List<Card>> recordMap = Maps.newHashMap();
|
||||
@@ -187,7 +173,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
||||
if (random) {
|
||||
p = Aggregates.random(game.getPlayers());
|
||||
} else {
|
||||
p = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(game.getPlayers(), sa, Localizer.getInstance().getMessage("lblChoosePlayer"));
|
||||
p = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(game.getPlayers(), sa, Localizer.getInstance().getMessage("lblChoosePlayer"), null);
|
||||
}
|
||||
if (recordMap.containsKey(p)) {
|
||||
recordMap.get(p).add(0, card);
|
||||
@@ -208,7 +194,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
||||
valid = CardLists.filterControlledBy(valid,
|
||||
game.getNextPlayerAfter(p, source.getChosenDirection()));
|
||||
}
|
||||
Card card = p.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseaCard"));
|
||||
Card card = p.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseaCard"), null);
|
||||
if (recordMap.containsKey(p)) {
|
||||
recordMap.get(p).add(0, card);
|
||||
} else {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user