- Basic support for CountersMoveAi (e.g. The Ozolith) so at least it doesn't crash, needs improvement.

This commit is contained in:
Hans Mackowiak
2020-05-27 00:17:28 +00:00
committed by Sol
parent 955a16f1e0
commit 6fde7149a4
180 changed files with 1995 additions and 1619 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}
@@ -382,13 +382,13 @@ public class ComputerUtil {
// 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);
}
&& 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
@@ -454,10 +454,10 @@ public class ComputerUtil {
// 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);
}
&& 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
@@ -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) {
@@ -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)) {
@@ -1010,7 +1010,7 @@ public class ComputerUtil {
}
if (card.hasKeyword(Keyword.EXALTED)) {
return true;
return true;
}
//cast equipments in Main1 when there are creatures to equip and no other unequipped equipment
@@ -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,13 +1270,13 @@ 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) {
@@ -1332,7 +1332,7 @@ public class ComputerUtil {
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;
@@ -1343,8 +1343,8 @@ public class ComputerUtil {
for (Trigger t : c.getTriggers()) {
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;
}
@@ -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;
}
@@ -1813,7 +1813,7 @@ public class ComputerUtil {
}
//GainControl
else if ((threatApi == ApiType.GainControl
|| (threatApi == ApiType.Attach && topStack.hasParam("AILogic") && topStack.getParam("AILogic").equals("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) {
@@ -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());
@@ -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";
}
@@ -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) {

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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++) {

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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++) {

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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())) {

View File

@@ -371,9 +371,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
return false;
}
if ("Atarka's Command".equals(sourceName)
&& (list.size() < 2 || ai.getLandsPlayedThisTurn() < 1)) {
// be strict on playing lands off charms
return false;
&& (list.size() < 2 || ai.getLandsPlayedThisTurn() < 1)) {
// be strict on playing lands off charms
return false;
}
String num = sa.getParam("ChangeNum");
@@ -402,7 +402,7 @@ 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,7 +418,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
if (ComputerUtil.waitForBlocking(sa)) {
return false;
return false;
}
final AbilitySub subAb = sa.getSubAbility();
@@ -618,7 +618,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
*/
private static Card chooseCreature(final Player ai, CardCollection list) {
// Creating a new combat for testing purposes.
final Player opponent = ai.getWeakestOpponent();
final Player opponent = ai.getWeakestOpponent();
Combat combat = new Combat(opponent);
for (Card att : opponent.getCreaturesInPlay()) {
combat.addAttacker(att, ai);
@@ -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);
@@ -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()
@@ -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;
@@ -1617,7 +1617,7 @@ public class ChangeZoneAi 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) {
// Called when looking for creature to attach aura or equipment
return ComputerUtilCard.getBestAI(options);
}
@@ -1626,7 +1626,7 @@ public class ChangeZoneAi 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) {
// 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);

View File

@@ -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);
}
}

View File

@@ -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);
@@ -145,7 +146,7 @@ public class ChooseCardAi 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) {
final Card host = sa.getHostCard();
final Player ctrl = host.getController();
final String logic = sa.getParam("AILogic");

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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
@@ -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;
}

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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);

View File

@@ -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();

View File

@@ -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());

View File

@@ -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);

View File

@@ -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;
@@ -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);

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
@@ -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) {
@@ -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);
@@ -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
@@ -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));
}
});
@@ -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.
@@ -889,7 +889,7 @@ 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);
@@ -897,16 +897,20 @@ public class CountersPutAi extends SpellAbilityAi {
}
@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);
}
}

View File

@@ -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()) {
@@ -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
@@ -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;
}

View File

@@ -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));
@@ -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;
}
}
@@ -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;
}
}

View File

@@ -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;
@@ -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) {

View File

@@ -48,7 +48,7 @@ public class DamageDealAi extends DamageAiBase {
}
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);
@@ -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)) {
/*
@@ -198,7 +198,7 @@ public class DamageDealAi extends DamageAiBase {
}
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);
@@ -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;

View File

@@ -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);
}
});
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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));
}
});

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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")) {

View File

@@ -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());

View File

@@ -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()));

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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;
@@ -200,6 +201,18 @@ public abstract class SpellAbilityEffect {
: 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); }

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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));
}
}
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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")) {

View File

@@ -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()) {

View File

@@ -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)) {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,6 +59,8 @@ public class CountersMoveEffect extends SpellAbilityEffect {
} else {
sb.append(amount).append(" ").append(" counter");
}
} else if ("All".equals(countername)) {
sb.append("all counter");
} else {
sb.append(amount).append(" ").append(countername).append(" counter");
}
@@ -52,9 +68,9 @@ public class CountersMoveEffect extends SpellAbilityEffect {
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()));
}
@@ -72,10 +88,10 @@ public class CountersMoveEffect extends SpellAbilityEffect {
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;
}
@@ -95,11 +111,6 @@ public class CountersMoveEffect extends SpellAbilityEffect {
}
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);
}
}
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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);

View File

@@ -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()));
}

View File

@@ -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)
@@ -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);

View File

@@ -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)));
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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(".");

View File

@@ -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