This commit is contained in:
Alessandro Coli
2019-06-16 08:54:25 +02:00
406 changed files with 661 additions and 350 deletions

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.26-SNAPSHOT</version> <version>1.6.27-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-ai</artifactId> <artifactId>forge-ai</artifactId>

View File

@@ -1778,7 +1778,7 @@ public class AiController {
+ MyRandom.getRandom().nextInt(3); + MyRandom.getRandom().nextInt(3);
return Math.max(remaining, min) / 2; return Math.max(remaining, min) / 2;
} else if ("LowestLoseLife".equals(logic)) { } else if ("LowestLoseLife".equals(logic)) {
return MyRandom.getRandom().nextInt(Math.min(player.getLife() / 3, ComputerUtil.getOpponentFor(player).getLife())) + 1; return MyRandom.getRandom().nextInt(Math.min(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1;
} else if ("HighestGetCounter".equals(logic)) { } else if ("HighestGetCounter".equals(logic)) {
return MyRandom.getRandom().nextInt(3); return MyRandom.getRandom().nextInt(3);
} else if (source.hasSVar("EnergyToPay")) { } else if (source.hasSVar("EnergyToPay")) {

View File

@@ -521,7 +521,7 @@ public class ComputerUtilCard {
*/ */
public static CardCollectionView getLikelyBlockers(final Player ai, final CardCollectionView blockers) { public static CardCollectionView getLikelyBlockers(final Player ai, final CardCollectionView blockers) {
AiBlockController aiBlk = new AiBlockController(ai); AiBlockController aiBlk = new AiBlockController(ai);
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
Combat combat = new Combat(opp); Combat combat = new Combat(opp);
//Use actual attackers if available, else consider all possible attackers //Use actual attackers if available, else consider all possible attackers
Combat currentCombat = ai.getGame().getCombat(); Combat currentCombat = ai.getGame().getCombat();
@@ -884,7 +884,7 @@ public class ComputerUtilCard {
List<String> chosen = new ArrayList<String>(); List<String> chosen = new ArrayList<String>();
Player ai = sa.getActivatingPlayer(); Player ai = sa.getActivatingPlayer();
final Game game = ai.getGame(); final Game game = ai.getGame();
Player opp = ComputerUtil.getOpponentFor(ai); Player opp = ai.getWeakestOpponent();
if (sa.hasParam("AILogic")) { if (sa.hasParam("AILogic")) {
final String logic = sa.getParam("AILogic"); final String logic = sa.getParam("AILogic");
@@ -974,7 +974,7 @@ public class ComputerUtilCard {
public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) { public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) {
final Player ai = sa.getActivatingPlayer(); final Player ai = sa.getActivatingPlayer();
final AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); final AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
final Game game = ai.getGame(); final Game game = ai.getGame();
final PhaseHandler ph = game.getPhaseHandler(); final PhaseHandler ph = game.getPhaseHandler();
final PhaseType phaseType = ph.getPhase(); final PhaseType phaseType = ph.getPhase();
@@ -1269,7 +1269,7 @@ public class ComputerUtilCard {
} }
} }
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
Card pumped = getPumpedCreature(ai, sa, c, toughness, power, keywords); Card pumped = getPumpedCreature(ai, sa, c, toughness, power, keywords);
List<Card> oppCreatures = opp.getCreaturesInPlay(); List<Card> oppCreatures = opp.getCreaturesInPlay();
float chance = 0; float chance = 0;
@@ -1842,17 +1842,45 @@ public class ComputerUtilCard {
String needsToPlayName = isRightSplit ? "SplitNeedsToPlay" : "NeedsToPlay"; String needsToPlayName = isRightSplit ? "SplitNeedsToPlay" : "NeedsToPlay";
String needsToPlayVarName = isRightSplit ? "SplitNeedsToPlayVar" : "NeedsToPlayVar"; String needsToPlayVarName = isRightSplit ? "SplitNeedsToPlayVar" : "NeedsToPlayVar";
if (sa != null && sa.isEvoke()) { // TODO: if there are ever split cards with Evoke or Kicker, factor in the right split option above
if (card.hasSVar("NeedsToPlayEvoked")) { if (sa != null) {
needsToPlayName = "NeedsToPlayEvoked"; if (sa.isEvoke()) {
} // if the spell is evoked, will use NeedsToPlayEvoked if available (otherwise falls back to NeedsToPlay)
if (card.hasSVar("NeedsToPlayEvokedVar")) { if (card.hasSVar("NeedsToPlayEvoked")) {
needsToPlayVarName = "NeedsToPlayEvokedVar"; needsToPlayName = "NeedsToPlayEvoked";
}
if (card.hasSVar("NeedsToPlayEvokedVar")) {
needsToPlayVarName = "NeedsToPlayEvokedVar";
}
} else if (sa.isKicked()) {
// if the spell is kicked, uses NeedsToPlayKicked if able and locks out the regular NeedsToPlay check
// for unkicked spells, uses NeedsToPlay
if (card.hasSVar("NeedsToPlayKicked")) {
needsToPlayName = "NeedsToPlayKicked";
} else {
needsToPlayName = "UNUSED";
}
if (card.hasSVar("NeedsToPlayKickedVar")) {
needsToPlayVarName = "NeedsToPlayKickedVar";
} else {
needsToPlayVarName = "UNUSED";
}
} }
} }
if (card.hasSVar(needsToPlayName)) { if (card.hasSVar(needsToPlayName)) {
final String needsToPlay = card.getSVar(needsToPlayName); final String needsToPlay = card.getSVar(needsToPlayName);
// A special case which checks that this creature will attack if it's the AI's turn
if (needsToPlay.equalsIgnoreCase("WillAttack")) {
if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
return ComputerUtilCard.doesSpecifiedCreatureAttackAI(sa.getActivatingPlayer(), card) ?
AiPlayDecision.WillPlay : AiPlayDecision.BadEtbEffects;
} else {
return AiPlayDecision.WillPlay; // not our turn, skip this check for the possible Flash use etc.
}
}
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield); CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
list = CardLists.getValidCards(list, needsToPlay.split(","), card.getController(), card, null); list = CardLists.getValidCards(list, needsToPlay.split(","), card.getController(), card, null);

View File

@@ -635,7 +635,7 @@ public class ComputerUtilCost {
&& (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2) && (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2)
&& (!source.getName().equals("Perplex") || payer.getCardsIn(ZoneType.Hand).size() < 2) && (!source.getName().equals("Perplex") || payer.getCardsIn(ZoneType.Hand).size() < 2)
&& (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1) && (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1)
&& (!source.getName().equals("Chain of Vapor") || (ComputerUtil.getOpponentFor(payer).getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3)); && (!source.getName().equals("Chain of Vapor") || (payer.getWeakestOpponent().getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3));
} }
public static Set<String> getAvailableManaColors(Player ai, Card additionalLand) { public static Set<String> getAvailableManaColors(Player ai, Card additionalLand) {

View File

@@ -1105,7 +1105,7 @@ public class PlayerControllerAi extends PlayerController {
public String chooseCardName(SpellAbility sa, Predicate<ICardFace> cpp, String valid, String message) { public String chooseCardName(SpellAbility sa, Predicate<ICardFace> cpp, String valid, String message) {
if (sa.hasParam("AILogic")) { if (sa.hasParam("AILogic")) {
CardCollectionView aiLibrary = player.getCardsIn(ZoneType.Library); CardCollectionView aiLibrary = player.getCardsIn(ZoneType.Library);
CardCollectionView oppLibrary = ComputerUtil.getOpponentFor(player).getCardsIn(ZoneType.Library); CardCollectionView oppLibrary = player.getWeakestOpponent().getCardsIn(ZoneType.Library);
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final String logic = sa.getParam("AILogic"); final String logic = sa.getParam("AILogic");
@@ -1227,6 +1227,18 @@ public class PlayerControllerAi extends PlayerController {
// Choose the optional cost if it can be paid (to be improved later, check for playability and other conditions perhaps) // Choose the optional cost if it can be paid (to be improved later, check for playability and other conditions perhaps)
Cost fullCost = opt.getCost().copy().add(costSoFar); Cost fullCost = opt.getCost().copy().add(costSoFar);
SpellAbility fullCostSa = chosen.copyWithDefinedCost(fullCost); SpellAbility fullCostSa = chosen.copyWithDefinedCost(fullCost);
// Playability check for Kicker
if (opt.getType() == OptionalCost.Kicker1 || opt.getType() == OptionalCost.Kicker2) {
SpellAbility kickedSaCopy = fullCostSa.copy();
kickedSaCopy.addOptionalCost(opt.getType());
Card copy = CardUtil.getLKICopy(chosen.getHostCard());
copy.addOptionalCostPaid(opt.getType());
if (ComputerUtilCard.checkNeedsToPlayReqs(copy, kickedSaCopy) != AiPlayDecision.WillPlay) {
continue; // don't choose kickers we don't want to play
}
}
if (ComputerUtilCost.canPayCost(fullCostSa, player)) { if (ComputerUtilCost.canPayCost(fullCostSa, player)) {
chosenOptCosts.add(opt); chosenOptCosts.add(opt);
costSoFar.add(opt.getCost()); costSoFar.add(opt.getCost());

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
@@ -22,7 +21,7 @@ public class ActivateAbilityAi extends SpellAbilityAi {
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn()); boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
List<Card> list = CardLists.getType(opp.getCardsIn(ZoneType.Battlefield), sa.getParamOrDefault("Type", "Card")); List<Card> list = CardLists.getType(opp.getCardsIn(ZoneType.Battlefield), sa.getParamOrDefault("Type", "Card"));
@@ -46,7 +45,7 @@ public class ActivateAbilityAi extends SpellAbilityAi {
@Override @Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
@@ -87,7 +86,7 @@ public class ActivateAbilityAi extends SpellAbilityAi {
} }
} else { } else {
sa.resetTargets(); sa.resetTargets();
sa.getTargets().add(ComputerUtil.getOpponentFor(ai)); sa.getTargets().add(ai.getWeakestOpponent());
} }
return randomReturn; return randomReturn;

View File

@@ -78,13 +78,13 @@ public class AnimateAi extends SpellAbilityAi {
num = (num == null) ? "1" : num; num = (num == null) ? "1" : num;
final int nToSac = AbilityUtils.calculateAmount(topStack.getHostCard(), num, topStack); final int nToSac = AbilityUtils.calculateAmount(topStack.getHostCard(), num, topStack);
CardCollection list = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), CardCollection list = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","),
ComputerUtil.getOpponentFor(ai), topStack.getHostCard(), topStack); ai.getWeakestOpponent(), topStack.getHostCard(), topStack);
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack)); list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
ComputerUtilCard.sortByEvaluateCreature(list); ComputerUtilCard.sortByEvaluateCreature(list);
if (!list.isEmpty() && list.size() == nToSac && ComputerUtilCost.canPayCost(sa, ai)) { if (!list.isEmpty() && list.size() == nToSac && ComputerUtilCost.canPayCost(sa, ai)) {
Card animatedCopy = becomeAnimated(source, sa); Card animatedCopy = becomeAnimated(source, sa);
list.add(animatedCopy); list.add(animatedCopy);
list = CardLists.getValidCards(list, valid.split(","), ComputerUtil.getOpponentFor(ai), topStack.getHostCard(), list = CardLists.getValidCards(list, valid.split(","), ai.getWeakestOpponent(), topStack.getHostCard(),
topStack); topStack);
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack)); list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
if (ComputerUtilCard.evaluateCreature(animatedCopy) < ComputerUtilCard.evaluateCreature(list.get(0)) if (ComputerUtilCard.evaluateCreature(animatedCopy) < ComputerUtilCard.evaluateCreature(list.get(0))

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
import forge.game.card.CardLists; import forge.game.card.CardLists;
@@ -17,7 +16,7 @@ public class BalanceAi extends SpellAbilityAi {
int diff = 0; int diff = 0;
// TODO Add support for multiplayer logic // TODO Add support for multiplayer logic
final Player opp = ComputerUtil.getOpponentFor(aiPlayer); final Player opp = aiPlayer.getWeakestOpponent();
final CardCollectionView humPerms = opp.getCardsIn(ZoneType.Battlefield); final CardCollectionView humPerms = opp.getCardsIn(ZoneType.Battlefield);
final CardCollectionView compPerms = aiPlayer.getCardsIn(ZoneType.Battlefield); final CardCollectionView compPerms = aiPlayer.getCardsIn(ZoneType.Battlefield);

View File

@@ -2,7 +2,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.Game; import forge.game.Game;
@@ -25,7 +24,7 @@ public class BidLifeAi extends SpellAbilityAi {
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
if (tgt.canTgtCreature()) { if (tgt.canTgtCreature()) {
List<Card> list = CardLists.getTargetableCards(ComputerUtil.getOpponentFor(aiPlayer).getCardsIn(ZoneType.Battlefield), sa); List<Card> list = CardLists.getTargetableCards(aiPlayer.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), sa);
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa); list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
if (list.isEmpty()) { if (list.isEmpty()) {
return false; return false;

View File

@@ -229,7 +229,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa); final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
ZoneType origin = null; ZoneType origin = null;
final Player opponent = ComputerUtil.getOpponentFor(ai); final Player opponent = ai.getWeakestOpponent();
boolean activateForCost = ComputerUtil.activateForCost(sa, ai); boolean activateForCost = ComputerUtil.activateForCost(sa, ai);
if (sa.hasParam("Origin")) { if (sa.hasParam("Origin")) {
@@ -439,7 +439,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
// if putting cards from hand to library and parent is drawing cards // if putting cards from hand to library and parent is drawing cards
// make sure this will actually do something: // make sure this will actually do something:
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
final Player opp = ComputerUtil.getOpponentFor(aiPlayer); final Player opp = aiPlayer.getWeakestOpponent();
if (tgt != null && tgt.canTgtPlayer()) { if (tgt != null && tgt.canTgtPlayer()) {
boolean isCurse = sa.isCurse(); boolean isCurse = sa.isCurse();
if (isCurse && sa.canTarget(opp)) { if (isCurse && sa.canTarget(opp)) {
@@ -500,7 +500,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
Iterable<Player> pDefined; Iterable<Player> pDefined;
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
if ((tgt != null) && tgt.canTgtPlayer()) { if ((tgt != null) && tgt.canTgtPlayer()) {
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
if (sa.isCurse()) { if (sa.isCurse()) {
if (sa.canTarget(opp)) { if (sa.canTarget(opp)) {
sa.getTargets().add(opp); sa.getTargets().add(opp);
@@ -619,7 +619,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
*/ */
private static Card chooseCreature(final Player ai, CardCollection list) { private static Card chooseCreature(final Player ai, CardCollection list) {
// Creating a new combat for testing purposes. // Creating a new combat for testing purposes.
final Player opponent = ComputerUtil.getOpponentFor(ai); final Player opponent = ai.getWeakestOpponent();
Combat combat = new Combat(opponent); Combat combat = new Combat(opponent);
for (Card att : opponent.getCreaturesInPlay()) { for (Card att : opponent.getCreaturesInPlay()) {
combat.addAttacker(att, ai); combat.addAttacker(att, ai);
@@ -935,7 +935,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
&& !currCombat.getBlockers(attacker).isEmpty()) { && !currCombat.getBlockers(attacker).isEmpty()) {
ComputerUtilCard.sortByEvaluateCreature(blockers); ComputerUtilCard.sortByEvaluateCreature(blockers);
Combat combat = new Combat(ai); Combat combat = new Combat(ai);
combat.addAttacker(attacker, ComputerUtil.getOpponentFor(ai)); combat.addAttacker(attacker, ai.getWeakestOpponent());
for (Card blocker : blockers) { for (Card blocker : blockers) {
combat.addBlocker(attacker, blocker); combat.addBlocker(attacker, blocker);
} }

View File

@@ -8,7 +8,6 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilAbility; import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
@@ -127,7 +126,7 @@ public class ChooseCardAi extends SpellAbilityAi {
} }
} else if (aiLogic.equals("Duneblast")) { } else if (aiLogic.equals("Duneblast")) {
CardCollection aiCreatures = ai.getCreaturesInPlay(); CardCollection aiCreatures = ai.getCreaturesInPlay();
CardCollection oppCreatures = ComputerUtil.getOpponentFor(ai).getCreaturesInPlay(); CardCollection oppCreatures = ai.getWeakestOpponent().getCreaturesInPlay();
aiCreatures = CardLists.getNotKeyword(aiCreatures, Keyword.INDESTRUCTIBLE); aiCreatures = CardLists.getNotKeyword(aiCreatures, Keyword.INDESTRUCTIBLE);
oppCreatures = CardLists.getNotKeyword(oppCreatures, Keyword.INDESTRUCTIBLE); oppCreatures = CardLists.getNotKeyword(oppCreatures, Keyword.INDESTRUCTIBLE);

View File

@@ -41,7 +41,7 @@ public class ChooseCardNameAi extends SpellAbilityAi {
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
if (tgt.canOnlyTgtOpponent()) { if (tgt.canOnlyTgtOpponent()) {
sa.getTargets().add(ComputerUtil.getOpponentFor(ai)); sa.getTargets().add(ai.getWeakestOpponent());
} else { } else {
sa.getTargets().add(ai); sa.getTargets().add(ai);
} }

View File

@@ -52,7 +52,7 @@ public class ChooseColorAi extends SpellAbilityAi {
} }
if ("Addle".equals(sourceName)) { if ("Addle".equals(sourceName)) {
if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) || ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Hand).isEmpty()) { if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) || ai.getWeakestOpponent().getCardsIn(ZoneType.Hand).isEmpty()) {
return false; return false;
} }
return true; return true;
@@ -61,7 +61,7 @@ public class ChooseColorAi extends SpellAbilityAi {
if (logic.equals("MostExcessOpponentControls")) { if (logic.equals("MostExcessOpponentControls")) {
for (byte color : MagicColor.WUBRG) { for (byte color : MagicColor.WUBRG) {
CardCollectionView ailist = ai.getCardsIn(ZoneType.Battlefield); CardCollectionView ailist = ai.getCardsIn(ZoneType.Battlefield);
CardCollectionView opplist = ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield); CardCollectionView opplist = ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield);
ailist = CardLists.filter(ailist, CardPredicates.isColor(color)); ailist = CardLists.filter(ailist, CardPredicates.isColor(color));
opplist = CardLists.filter(opplist, CardPredicates.isColor(color)); opplist = CardLists.filter(opplist, CardPredicates.isColor(color));

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -17,7 +16,7 @@ public class ChooseNumberAi extends SpellAbilityAi {
TargetRestrictions tgt = sa.getTargetRestrictions(); TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
Player opp = ComputerUtil.getOpponentFor(aiPlayer); Player opp = aiPlayer.getWeakestOpponent();
if (sa.canTarget(opp)) { if (sa.canTarget(opp)) {
sa.getTargets().add(opp); sa.getTargets().add(opp);
} else { } else {

View File

@@ -7,7 +7,6 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat; import forge.ai.ComputerUtilCombat;
import forge.ai.ComputerUtilCost; import forge.ai.ComputerUtilCost;
@@ -68,7 +67,7 @@ public class ChooseSourceAi extends SpellAbilityAi {
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
Player opp = ComputerUtil.getOpponentFor(ai); Player opp = ai.getWeakestOpponent();
if (sa.canTarget(opp)) { if (sa.canTarget(opp)) {
sa.getTargets().add(opp); sa.getTargets().add(opp);
} else { } else {

View File

@@ -3,7 +3,6 @@ package forge.ai.ability;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
@@ -30,7 +29,7 @@ public class ControlExchangeAi extends SpellAbilityAi {
sa.resetTargets(); sa.resetTargets();
CardCollection list = CardCollection list =
CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa); CardLists.getValidCards(ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
// AI won't try to grab cards that are filtered out of AI decks on // AI won't try to grab cards that are filtered out of AI decks on
// purpose // purpose
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {

View File

@@ -2,7 +2,6 @@ package forge.ai.ability;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCost; import forge.ai.ComputerUtilCost;
import forge.ai.ComputerUtilMana; import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
@@ -39,7 +38,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
final boolean curse = sa.isCurse(); final boolean curse = sa.isCurse();
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
hList = CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), valid, source.getController(), source); hList = CardLists.getValidCards(ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), valid, source.getController(), source);
cList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source); cList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source);
if (abCost != null) { if (abCost != null) {
@@ -68,7 +67,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
} }
if (tgt != null) { if (tgt != null) {
Player pl = curse ? ComputerUtil.getOpponentFor(ai) : ai; Player pl = curse ? ai.getWeakestOpponent() : ai;
sa.getTargets().add(pl); sa.getTargets().add(pl);
hList = CardLists.filterControlledBy(hList, pl); hList = CardLists.filterControlledBy(hList, pl);
@@ -149,7 +148,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
*/ */
@Override @Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return player.getCreaturesInPlay().size() >= ComputerUtil.getOpponentFor(player).getCreaturesInPlay().size(); return player.getCreaturesInPlay().size() >= player.getWeakestOpponent().getCreaturesInPlay().size();
} }
@Override @Override

View File

@@ -1,7 +1,6 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCombat; import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.Game; import forge.game.Game;
@@ -20,7 +19,7 @@ import forge.util.MyRandom;
public abstract class DamageAiBase extends SpellAbilityAi { public abstract class DamageAiBase extends SpellAbilityAi {
protected boolean avoidTargetP(final Player comp, final SpellAbility sa) { protected boolean avoidTargetP(final Player comp, final SpellAbility sa) {
Player enemy = ComputerUtil.getOpponentFor(comp); Player enemy = comp.getWeakestOpponent();
// Logic for cards that damage owner, like Fireslinger // Logic for cards that damage owner, like Fireslinger
// Do not target a player if they aren't below 75% of our health. // Do not target a player if they aren't below 75% of our health.
// Unless Lifelink will cancel the damage to us // Unless Lifelink will cancel the damage to us
@@ -54,7 +53,7 @@ public abstract class DamageAiBase extends SpellAbilityAi {
protected boolean shouldTgtP(final Player comp, final SpellAbility sa, final int d, final boolean noPrevention, final boolean noPlaneswalkerRedirection) { protected boolean shouldTgtP(final Player comp, final SpellAbility sa, final int d, final boolean noPrevention, final boolean noPlaneswalkerRedirection) {
int restDamage = d; int restDamage = d;
final Game game = comp.getGame(); final Game game = comp.getGame();
Player enemy = ComputerUtil.getOpponentFor(comp); Player enemy = comp.getWeakestOpponent();
boolean dmgByCardsInHand = false; boolean dmgByCardsInHand = false;
if ("X".equals(sa.getParam("NumDmg")) && sa.getHostCard() != null && sa.hasSVar(sa.getParam("NumDmg")) && if ("X".equals(sa.getParam("NumDmg")) && sa.getHostCard() != null && sa.hasSVar(sa.getParam("NumDmg")) &&

View File

@@ -212,7 +212,7 @@ public class DamageAllAi extends SpellAbilityAi {
} }
// Evaluate creatures getting killed // Evaluate creatures getting killed
Player enemy = ComputerUtil.getOpponentFor(ai); Player enemy = ai.getWeakestOpponent();
final CardCollection humanList = getKillableCreatures(sa, enemy, dmg); final CardCollection humanList = getKillableCreatures(sa, enemy, dmg);
CardCollection computerList = getKillableCreatures(sa, ai, dmg); CardCollection computerList = getKillableCreatures(sa, ai, dmg);
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
@@ -294,7 +294,7 @@ public class DamageAllAi extends SpellAbilityAi {
} }
// Evaluate creatures getting killed // Evaluate creatures getting killed
Player enemy = ComputerUtil.getOpponentFor(ai); Player enemy = ai.getWeakestOpponent();
final CardCollection humanList = getKillableCreatures(sa, enemy, dmg); final CardCollection humanList = getKillableCreatures(sa, enemy, dmg);
CardCollection computerList = getKillableCreatures(sa, ai, dmg); CardCollection computerList = getKillableCreatures(sa, ai, dmg);
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();

View File

@@ -552,7 +552,7 @@ public class DamageDealAi extends DamageAiBase {
final boolean oppTargetsChoice = sa.hasParam("TargetingPlayer"); final boolean oppTargetsChoice = sa.hasParam("TargetingPlayer");
final String logic = sa.getParamOrDefault("AILogic", ""); final String logic = sa.getParamOrDefault("AILogic", "");
Player enemy = ComputerUtil.getOpponentFor(ai); Player enemy = ai.getWeakestOpponent();
if ("PowerDmg".equals(logic)) { if ("PowerDmg".equals(logic)) {
// check if it is better to target the player instead, the original target is already set in PumpAi.pumpTgtAI() // check if it is better to target the player instead, the original target is already set in PumpAi.pumpTgtAI()
@@ -876,7 +876,7 @@ public class DamageDealAi extends DamageAiBase {
// this is for Triggered targets that are mandatory // this is for Triggered targets that are mandatory
final boolean noPrevention = sa.hasParam("NoPrevention"); final boolean noPrevention = sa.hasParam("NoPrevention");
final boolean divided = sa.hasParam("DividedAsYouChoose"); final boolean divided = sa.hasParam("DividedAsYouChoose");
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) { while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
if (tgt.canTgtPlaneswalker()) { if (tgt.canTgtPlaneswalker()) {

View File

@@ -4,7 +4,6 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost; import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
@@ -177,7 +176,7 @@ public class DebuffAi extends SpellAbilityAi {
* @return a CardCollection. * @return a CardCollection.
*/ */
private CardCollection getCurseCreatures(final Player ai, final SpellAbility sa, final List<String> kws) { private CardCollection getCurseCreatures(final Player ai, final SpellAbility sa, final List<String> kws) {
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
CardCollection list = CardLists.getTargetableCards(opp.getCreaturesInPlay(), sa); CardCollection list = CardLists.getTargetableCards(opp.getCreaturesInPlay(), sa);
if (!list.isEmpty()) { if (!list.isEmpty()) {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {
@@ -217,7 +216,7 @@ public class DebuffAi extends SpellAbilityAi {
list.remove(c); list.remove(c);
} }
final CardCollection pref = CardLists.filterControlledBy(list, ComputerUtil.getOpponentFor(ai)); final CardCollection pref = CardLists.filterControlledBy(list, ai.getWeakestOpponent());
final CardCollection forced = CardLists.filterControlledBy(list, ai); final CardCollection forced = CardLists.filterControlledBy(list, ai);
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();

View File

@@ -288,7 +288,7 @@ public class DestroyAi extends SpellAbilityAi {
} else if (sa.hasParam("Defined")) { } else if (sa.hasParam("Defined")) {
list = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa); list = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
if ("WillSkipTurn".equals(logic) && (sa.getHostCard().getController().equals(ai) if ("WillSkipTurn".equals(logic) && (sa.getHostCard().getController().equals(ai)
|| ai.getCreaturesInPlay().size() < ComputerUtil.getOpponentFor(ai).getCreaturesInPlay().size() || ai.getCreaturesInPlay().size() < ai.getWeakestOpponent().getCreaturesInPlay().size()
|| !source.getGame().getPhaseHandler().isPlayerTurn(ai) || !source.getGame().getPhaseHandler().isPlayerTurn(ai)
|| ai.getLife() <= 5)) { || ai.getLife() <= 5)) {
// Basic ai logic for Lethal Vapors // Basic ai logic for Lethal Vapors

View File

@@ -66,7 +66,7 @@ public class DestroyAllAi extends SpellAbilityAi {
public boolean doMassRemovalLogic(Player ai, SpellAbility sa) { public boolean doMassRemovalLogic(Player ai, SpellAbility sa) {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final String logic = sa.getParamOrDefault("AILogic", ""); final String logic = sa.getParamOrDefault("AILogic", "");
Player opponent = ComputerUtil.getOpponentFor(ai); // TODO: how should this AI logic work for multiplayer and getOpponents()? Player opponent = ai.getWeakestOpponent(); // TODO: how should this AI logic work for multiplayer and getOpponents()?
final int CREATURE_EVAL_THRESHOLD = 200; final int CREATURE_EVAL_THRESHOLD = 200;

View File

@@ -24,7 +24,7 @@ public class DigAi extends SpellAbilityAi {
@Override @Override
protected boolean canPlayAI(Player ai, SpellAbility sa) { protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Game game = ai.getGame(); final Game game = ai.getGame();
Player opp = ComputerUtil.getOpponentFor(ai); Player opp = ai.getWeakestOpponent();
final Card host = sa.getHostCard(); final Card host = sa.getHostCard();
Player libraryOwner = ai; Player libraryOwner = ai;
@@ -106,7 +106,7 @@ public class DigAi extends SpellAbilityAi {
@Override @Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
if (sa.usesTargeting()) { if (sa.usesTargeting()) {
sa.resetTargets(); sa.resetTargets();
if (mandatory && sa.canTarget(opp)) { if (mandatory && sa.canTarget(opp)) {

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilMana; import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.card.Card; import forge.game.card.Card;
@@ -36,7 +35,7 @@ public class DigUntilAi extends SpellAbilityAi {
final boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1); final boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
Player libraryOwner = ai; Player libraryOwner = ai;
Player opp = ComputerUtil.getOpponentFor(ai); Player opp = ai.getWeakestOpponent();
if ("DontMillSelf".equals(logic)) { if ("DontMillSelf".equals(logic)) {
// A card that digs for specific things and puts everything revealed before it into graveyard // A card that digs for specific things and puts everything revealed before it into graveyard

View File

@@ -56,7 +56,7 @@ public class DiscardAi extends SpellAbilityAi {
return SpecialCardAi.VolrathsShapeshifter.consider(ai, sa); return SpecialCardAi.VolrathsShapeshifter.consider(ai, sa);
} }
final boolean humanHasHand = ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Hand).size() > 0; final boolean humanHasHand = ai.getWeakestOpponent().getCardsIn(ZoneType.Hand).size() > 0;
if (tgt != null) { if (tgt != null) {
if (!discardTargetAI(ai, sa)) { if (!discardTargetAI(ai, sa)) {
@@ -87,7 +87,7 @@ public class DiscardAi extends SpellAbilityAi {
if (sa.hasParam("NumCards")) { if (sa.hasParam("NumCards")) {
if (sa.getParam("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")) { if (sa.getParam("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value. // Set PayX here to maximum value.
final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ComputerUtil.getOpponentFor(ai) final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ai.getWeakestOpponent()
.getCardsIn(ZoneType.Hand).size()); .getCardsIn(ZoneType.Hand).size());
if (cardsToDiscard < 1) { if (cardsToDiscard < 1) {
return false; return false;
@@ -172,7 +172,7 @@ public class DiscardAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) { if (tgt != null) {
Player opp = ComputerUtil.getOpponentFor(ai); Player opp = ai.getWeakestOpponent();
if (!discardTargetAI(ai, sa)) { if (!discardTargetAI(ai, sa)) {
if (mandatory && sa.canTarget(opp)) { if (mandatory && sa.canTarget(opp)) {
sa.getTargets().add(opp); sa.getTargets().add(opp);
@@ -193,7 +193,7 @@ public class DiscardAi extends SpellAbilityAi {
} }
if ("X".equals(sa.getParam("RevealNumber")) && sa.getHostCard().getSVar("X").equals("Count$xPaid")) { if ("X".equals(sa.getParam("RevealNumber")) && sa.getHostCard().getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value. // Set PayX here to maximum value.
final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ComputerUtil.getOpponentFor(ai) final int cardsToDiscard = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), ai.getWeakestOpponent()
.getCardsIn(ZoneType.Hand).size()); .getCardsIn(ZoneType.Hand).size());
sa.getHostCard().setSVar("PayX", Integer.toString(cardsToDiscard)); sa.getHostCard().setSVar("PayX", Integer.toString(cardsToDiscard));
} }

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
@@ -19,7 +18,7 @@ public class DrainManaAi extends SpellAbilityAi {
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn()); boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
if (tgt == null) { if (tgt == null) {
@@ -41,7 +40,7 @@ public class DrainManaAi extends SpellAbilityAi {
@Override @Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
@@ -82,7 +81,7 @@ public class DrainManaAi extends SpellAbilityAi {
} }
} else { } else {
sa.resetTargets(); sa.resetTargets();
sa.getTargets().add(ComputerUtil.getOpponentFor(ai)); sa.getTargets().add(ai.getWeakestOpponent());
} }
return randomReturn; return randomReturn;

View File

@@ -107,7 +107,7 @@ public class FogAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
final Game game = aiPlayer.getGame(); final Game game = aiPlayer.getGame();
boolean chance; boolean chance;
if (game.getPhaseHandler().isPlayerTurn(ComputerUtil.getOpponentFor(sa.getActivatingPlayer()))) { if (game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer().getWeakestOpponent())) {
chance = game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE); chance = game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE);
} else { } else {
chance = game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DAMAGE); chance = game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DAMAGE);

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -9,7 +8,7 @@ import forge.game.spellability.TargetRestrictions;
public class GameLossAi extends SpellAbilityAi { public class GameLossAi extends SpellAbilityAi {
@Override @Override
protected boolean canPlayAI(Player ai, SpellAbility sa) { protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
if (opp.cantLose()) { if (opp.cantLose()) {
return false; return false;
} }
@@ -34,15 +33,16 @@ public class GameLossAi extends SpellAbilityAi {
// Phage the Untouchable // Phage the Untouchable
// (Final Fortune would need to attach it's delayed trigger to a // (Final Fortune would need to attach it's delayed trigger to a
// specific turn, which can't be done yet) // specific turn, which can't be done yet)
Player opp = ai.getWeakestOpponent();
if (!mandatory && ComputerUtil.getOpponentFor(ai).cantLose()) { if (!mandatory && opp.cantLose()) {
return false; return false;
} }
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
sa.getTargets().add(ComputerUtil.getOpponentFor(ai)); sa.getTargets().add(opp);
} }
return true; return true;

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -20,7 +19,7 @@ public class LifeExchangeAi extends SpellAbilityAi {
@Override @Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) { protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
final int myLife = aiPlayer.getLife(); final int myLife = aiPlayer.getLife();
Player opponent = ComputerUtil.getOpponentFor(aiPlayer); Player opponent = aiPlayer.getWeakestOpponent();
final int hLife = opponent.getLife(); final int hLife = opponent.getLife();
if (!aiPlayer.canGainLife()) { if (!aiPlayer.canGainLife()) {
@@ -76,7 +75,7 @@ public class LifeExchangeAi extends SpellAbilityAi {
final boolean mandatory) { final boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
Player opp = ComputerUtil.getOpponentFor(ai); Player opp = ai.getWeakestOpponent();
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
if (sa.canTarget(opp) && (mandatory || ai.getLife() < opp.getLife())) { if (sa.canTarget(opp) && (mandatory || ai.getLife() < opp.getLife())) {

View File

@@ -149,7 +149,7 @@ public class LifeExchangeVariantAi extends SpellAbilityAi {
final boolean mandatory) { final boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
Player opp = ComputerUtil.getOpponentFor(ai); Player opp = ai.getWeakestOpponent();
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
if (sa.canTarget(opp) && (mandatory || ai.getLife() < opp.getLife())) { if (sa.canTarget(opp) && (mandatory || ai.getLife() < opp.getLife())) {

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilAbility; import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilMana; import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
@@ -20,7 +19,7 @@ public class LifeSetAi extends SpellAbilityAi {
// Ability_Cost abCost = sa.getPayCosts(); // Ability_Cost abCost = sa.getPayCosts();
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final int myLife = ai.getLife(); final int myLife = ai.getLife();
final Player opponent = ComputerUtil.getOpponentFor(ai); final Player opponent = ai.getWeakestOpponent();
final int hlife = opponent.getLife(); final int hlife = opponent.getLife();
final String amountStr = sa.getParam("LifeAmount"); final String amountStr = sa.getParam("LifeAmount");
@@ -107,7 +106,7 @@ public class LifeSetAi extends SpellAbilityAi {
@Override @Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final int myLife = ai.getLife(); final int myLife = ai.getLife();
final Player opponent = ComputerUtil.getOpponentFor(ai); final Player opponent = ai.getWeakestOpponent();
final int hlife = opponent.getLife(); final int hlife = opponent.getLife();
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa); final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);

View File

@@ -95,7 +95,7 @@ public class MustBlockAi extends SpellAbilityAi {
boolean chance = false; boolean chance = false;
if (abTgt != null) { if (abTgt != null) {
final List<Card> list = determineGoodBlockers(definedAttacker, ai, ComputerUtil.getOpponentFor(ai), sa, true,true); final List<Card> list = determineGoodBlockers(definedAttacker, ai, ai.getWeakestOpponent(), sa, true,true);
if (list.isEmpty()) { if (list.isEmpty()) {
return false; return false;
} }

View File

@@ -2,7 +2,6 @@ package forge.ai.ability;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
@@ -31,7 +30,7 @@ public class PowerExchangeAi extends SpellAbilityAi {
sa.resetTargets(); sa.resetTargets();
List<Card> list = List<Card> list =
CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa); CardLists.getValidCards(ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
// AI won't try to grab cards that are filtered out of AI decks on // AI won't try to grab cards that are filtered out of AI decks on
// purpose // purpose
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {

View File

@@ -146,7 +146,7 @@ public class ProtectAi extends SpellAbilityAi {
if (s==null) { if (s==null) {
return false; return false;
} else { } else {
Player opponent = ComputerUtil.getOpponentFor(ai); Player opponent = ai.getWeakestOpponent();
Combat combat = ai.getGame().getCombat(); Combat combat = ai.getGame().getCombat();
int dmg = ComputerUtilCombat.damageIfUnblocked(c, opponent, combat, true); int dmg = ComputerUtilCombat.damageIfUnblocked(c, opponent, combat, true);
float ratio = 1.0f * dmg / opponent.getLife(); float ratio = 1.0f * dmg / opponent.getLife();

View File

@@ -177,7 +177,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
final Game game = ai.getGame(); final Game game = ai.getGame();
final Combat combat = game.getCombat(); final Combat combat = game.getCombat();
final PhaseHandler ph = game.getPhaseHandler(); final PhaseHandler ph = game.getPhaseHandler();
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
final int newPower = card.getNetCombatDamage() + attack; final int newPower = card.getNetCombatDamage() + attack;
//int defense = getNumDefense(sa); //int defense = getNumDefense(sa);
if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) { if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) {

View File

@@ -68,7 +68,7 @@ public class PumpAllAi extends PumpAiBase {
valid = sa.getParam("ValidCards"); valid = sa.getParam("ValidCards");
} }
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
CardCollection comp = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source); CardCollection comp = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source);
CardCollection human = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source); CardCollection human = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source);

View File

@@ -46,10 +46,10 @@ public class RearrangeTopOfLibraryAi extends SpellAbilityAi {
// ability is targeted // ability is targeted
sa.resetTargets(); sa.resetTargets();
Player opp = ComputerUtil.getOpponentFor(aiPlayer); Player opp = aiPlayer.getWeakestOpponent();
final boolean canTgtAI = sa.canTarget(aiPlayer); final boolean canTgtAI = sa.canTarget(aiPlayer);
final boolean canTgtHuman = sa.canTarget(opp); final boolean canTgtHuman = sa.canTarget(opp);
if (canTgtHuman && canTgtAI) { if (canTgtHuman && canTgtAI) {
// TODO: maybe some other consideration rather than random? // TODO: maybe some other consideration rather than random?
Player preferredTarget = MyRandom.percentTrue(50) ? aiPlayer : opp; Player preferredTarget = MyRandom.percentTrue(50) ? aiPlayer : opp;

View File

@@ -15,7 +15,7 @@ public class RepeatAi extends SpellAbilityAi {
protected boolean canPlayAI(Player ai, SpellAbility sa) { protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
if (tgt != null) { if (tgt != null) {
if (!opp.canBeTargetedBy(sa)) { if (!opp.canBeTargetedBy(sa)) {
@@ -49,7 +49,7 @@ public class RepeatAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting()) { if (sa.usesTargeting()) {
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
if (sa.canTarget(opp)) { if (sa.canTarget(opp)) {
sa.resetTargets(); sa.resetTargets();
sa.getTargets().add(opp); sa.getTargets().add(opp);

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilMana; import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
@@ -63,7 +62,7 @@ public class SacrificeAi extends SpellAbilityAi {
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
final boolean destroy = sa.hasParam("Destroy"); final boolean destroy = sa.hasParam("Destroy");
Player opp = ComputerUtil.getOpponentFor(ai); Player opp = ai.getWeakestOpponent();
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
if (!opp.canBeTargetedBy(sa)) { if (!opp.canBeTargetedBy(sa)) {

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost; import forge.ai.ComputerUtilCost;
import forge.ai.ComputerUtilMana; import forge.ai.ComputerUtilMana;
@@ -37,7 +36,7 @@ public class SacrificeAllAi extends SpellAbilityAi {
} }
CardCollection humanlist = CardCollection humanlist =
CardLists.getValidCards(ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield), valid.split(","), source.getController(), source, sa); CardLists.getValidCards(ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), valid.split(","), source.getController(), source, sa);
CardCollection computerlist = CardCollection computerlist =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), source.getController(), source, sa); CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), source.getController(), source, sa);

View File

@@ -112,7 +112,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
* @return a boolean. * @return a boolean.
*/ */
protected boolean tapPrefTargeting(final Player ai, final Card source, final TargetRestrictions tgt, final SpellAbility sa, final boolean mandatory) { protected boolean tapPrefTargeting(final Player ai, final Card source, final TargetRestrictions tgt, final SpellAbility sa, final boolean mandatory) {
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
final Game game = ai.getGame(); final Game game = ai.getGame();
CardCollection tapList = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents()); CardCollection tapList = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
tapList = CardLists.getValidCards(tapList, tgt.getValidTgts(), source.getController(), source, sa); tapList = CardLists.getValidCards(tapList, tgt.getValidTgts(), source.getController(), source, sa);

View File

@@ -3,7 +3,6 @@ package forge.ai.ability;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCombat; import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.Game; import forge.game.Game;
@@ -30,7 +29,7 @@ public class TapAllAi extends SpellAbilityAi {
// or during upkeep/begin combat? // or during upkeep/begin combat?
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
final Game game = ai.getGame(); final Game game = ai.getGame();
if (game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_BEGIN)) { if (game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_BEGIN)) {
@@ -125,8 +124,9 @@ public class TapAllAi extends SpellAbilityAi {
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
sa.getTargets().add(ComputerUtil.getOpponentFor(ai)); Player opp = ai.getWeakestOpponent();
validTappables = ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield); sa.getTargets().add(opp);
validTappables = opp.getCardsIn(ZoneType.Battlefield);
} }
if (mandatory) { if (mandatory) {

View File

@@ -167,7 +167,7 @@ public class TokenAi extends SpellAbilityAi {
*/ */
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final Game game = ai.getGame(); final Game game = ai.getGame();
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
if (ComputerUtil.preventRunAwayActivations(sa)) { if (ComputerUtil.preventRunAwayActivations(sa)) {
return false; // prevent infinite tokens? return false; // prevent infinite tokens?
@@ -261,13 +261,13 @@ public class TokenAi extends SpellAbilityAi {
num = (num == null) ? "1" : num; num = (num == null) ? "1" : num;
final int nToSac = AbilityUtils.calculateAmount(topStack.getHostCard(), num, topStack); final int nToSac = AbilityUtils.calculateAmount(topStack.getHostCard(), num, topStack);
CardCollection list = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), CardCollection list = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","),
ComputerUtil.getOpponentFor(ai), topStack.getHostCard(), sa); ai.getWeakestOpponent(), topStack.getHostCard(), sa);
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack)); list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
// only care about saving single creature for now // only care about saving single creature for now
if (!list.isEmpty() && nTokens > 0 && list.size() == nToSac) { if (!list.isEmpty() && nTokens > 0 && list.size() == nToSac) {
ComputerUtilCard.sortByEvaluateCreature(list); ComputerUtilCard.sortByEvaluateCreature(list);
list.add(token); list.add(token);
list = CardLists.getValidCards(list, valid.split(","), ComputerUtil.getOpponentFor(ai), topStack.getHostCard(), sa); list = CardLists.getValidCards(list, valid.split(","), ai.getWeakestOpponent(), topStack.getHostCard(), sa);
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack)); list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
if (ComputerUtilCard.evaluateCreature(token) < ComputerUtilCard.evaluateCreature(list.get(0)) if (ComputerUtilCard.evaluateCreature(token) < ComputerUtilCard.evaluateCreature(list.get(0))
&& list.contains(token)) { && list.contains(token)) {
@@ -285,7 +285,7 @@ public class TokenAi extends SpellAbilityAi {
if (tgt != null) { if (tgt != null) {
sa.resetTargets(); sa.resetTargets();
if (tgt.canOnlyTgtOpponent()) { if (tgt.canOnlyTgtOpponent()) {
sa.getTargets().add(ComputerUtil.getOpponentFor(ai)); sa.getTargets().add(ai.getWeakestOpponent());
} else { } else {
sa.getTargets().add(ai); sa.getTargets().add(ai);
} }

View File

@@ -2,7 +2,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
@@ -29,7 +28,7 @@ public class TwoPilesAi extends SpellAbilityAi {
valid = sa.getParam("ValidCards"); valid = sa.getParam("ValidCards");
} }
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) { if (tgt != null) {

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilMana; import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
@@ -65,7 +64,7 @@ public class UnattachAllAi extends SpellAbilityAi {
@Override @Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Card card = sa.getHostCard(); final Card card = sa.getHostCard();
final Player opp = ComputerUtil.getOpponentFor(ai); final Player opp = ai.getWeakestOpponent();
// Check if there are any valid targets // Check if there are any valid targets
List<GameObject> targets = new ArrayList<GameObject>(); List<GameObject> targets = new ArrayList<GameObject>();
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();

View File

@@ -139,7 +139,7 @@ public class UntapAi extends SpellAbilityAi {
Player targetController = ai; Player targetController = ai;
if (sa.isCurse()) { if (sa.isCurse()) {
targetController = ComputerUtil.getOpponentFor(ai); targetController = ai.getWeakestOpponent();
} }
CardCollection list = CardLists.getTargetableCards(targetController.getCardsIn(ZoneType.Battlefield), sa); CardCollection list = CardLists.getTargetableCards(targetController.getCardsIn(ZoneType.Battlefield), sa);

View File

@@ -1,24 +1,13 @@
package forge.ai.simulation; package forge.ai.simulation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.google.common.collect.BiMap; import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap; import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import forge.LobbyPlayer; import forge.LobbyPlayer;
import forge.ai.LobbyPlayerAi; import forge.ai.LobbyPlayerAi;
import forge.card.CardStateName; import forge.card.CardStateName;
import forge.game.Game; import forge.game.*;
import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.GameObjectMap;
import forge.game.GameRules;
import forge.game.Match;
import forge.game.StaticEffect;
import forge.game.card.*; import forge.game.card.*;
import forge.game.card.token.TokenInfo; import forge.game.card.token.TokenInfo;
import forge.game.combat.Combat; import forge.game.combat.Combat;
@@ -28,13 +17,16 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.RegisteredPlayer; import forge.game.player.RegisteredPlayer;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityRestriction;
import forge.game.spellability.SpellAbilityStackInstance; import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.trigger.TriggerType; import forge.game.trigger.TriggerType;
import forge.game.zone.PlayerZoneBattlefield; import forge.game.zone.PlayerZoneBattlefield;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class GameCopier { public class GameCopier {
private static final ZoneType[] ZONES = new ZoneType[] { private static final ZoneType[] ZONES = new ZoneType[] {
ZoneType.Battlefield, ZoneType.Battlefield,
@@ -323,7 +315,6 @@ public class GameCopier {
} }
if (c.isPlaneswalker()) { if (c.isPlaneswalker()) {
for (SpellAbility sa : c.getAllSpellAbilities()) { for (SpellAbility sa : c.getAllSpellAbilities()) {
SpellAbilityRestriction restrict = sa.getRestrictions();
int active = sa.getActivationsThisTurn(); int active = sa.getActivationsThisTurn();
if (sa.isPwAbility() && active > 0) { if (sa.isPwAbility() && active > 0) {
SpellAbility newSa = findSAInCard(sa, newCard); SpellAbility newSa = findSAInCard(sa, newCard);

View File

@@ -207,7 +207,7 @@ public class GameSimulator {
} }
// TODO: Support multiple opponents. // TODO: Support multiple opponents.
Player opponent = ComputerUtil.getOpponentFor(aiPlayer); Player opponent = aiPlayer.getWeakestOpponent();
resolveStack(simGame, opponent); resolveStack(simGame, opponent);
// TODO: If this is during combat, before blockers are declared, // TODO: If this is during combat, before blockers are declared,

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.26-SNAPSHOT</version> <version>1.6.27-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-core</artifactId> <artifactId>forge-core</artifactId>

View File

@@ -7,8 +7,6 @@ import forge.card.CardRules;
import forge.card.CardSplitType; import forge.card.CardSplitType;
import forge.item.PaperCard; import forge.item.PaperCard;
import org.apache.commons.lang3.StringUtils;
public class ImageUtil { public class ImageUtil {
public static float getNearestHQSize(float baseSize, float actualSize) { public static float getNearestHQSize(float baseSize, float actualSize) {
//get nearest power of actualSize to baseSize so that the image renders good //get nearest power of actualSize to baseSize so that the image renders good
@@ -46,9 +44,7 @@ public class ImageUtil {
cntPictures = db.getPrintCount(card.getName(), edition); cntPictures = db.getPrintCount(card.getName(), edition);
hasManyPictures = cntPictures > 1; hasManyPictures = cntPictures > 1;
} else { } else {
// without set number of pictures equals number of urls provided in Svar:Picture cntPictures = 1;
String urls = card.getPictureUrl(backFace);
cntPictures = StringUtils.countMatches(urls, "\\") + 1;
// raise the art index limit to the maximum of the sets this card was printed in // raise the art index limit to the maximum of the sets this card was printed in
int maxCntPictures = db.getMaxPrintCount(card.getName()); int maxCntPictures = db.getMaxPrintCount(card.getName());

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.26-SNAPSHOT</version> <version>1.6.27-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-game</artifactId> <artifactId>forge-game</artifactId>

View File

@@ -17,18 +17,7 @@
*/ */
package forge.game.card; package forge.game.card;
import java.util.List; import com.google.common.collect.*;
import java.util.Set;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import io.sentry.Sentry;
import io.sentry.event.BreadcrumbBuilder;
import forge.ImageKeys; import forge.ImageKeys;
import forge.card.CardStateName; import forge.card.CardStateName;
import forge.card.CardType; import forge.card.CardType;
@@ -40,14 +29,16 @@ import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.AbilityManaPart; import forge.game.spellability.*;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.trigger.Trigger; import forge.game.trigger.Trigger;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.TextUtil; import forge.util.TextUtil;
import forge.util.collect.FCollection; import forge.util.collect.FCollection;
import io.sentry.Sentry;
import io.sentry.event.BreadcrumbBuilder;
import java.util.List;
import java.util.Set;
public final class CardUtil { public final class CardUtil {
// disable instantiation // disable instantiation
@@ -308,6 +299,15 @@ public final class CardUtil {
newCopy.updateKeywordsCache(newCopy.getState(s)); newCopy.updateKeywordsCache(newCopy.getState(s));
} }
newCopy.setKickerMagnitude(in.getKickerMagnitude());
for (OptionalCost ocost : in.getOptionalCostsPaid()) {
newCopy.addOptionalCostPaid(ocost);
}
newCopy.setCastSA(in.getCastSA());
newCopy.setCastFrom(in.getCastFrom());
return newCopy; return newCopy;
} }

View File

@@ -6,7 +6,7 @@
<packaging.type>jar</packaging.type> <packaging.type>jar</packaging.type>
<build.min.memory>-Xms1024m</build.min.memory> <build.min.memory>-Xms1024m</build.min.memory>
<build.max.memory>-Xmx1536m</build.max.memory> <build.max.memory>-Xmx1536m</build.max.memory>
<alpha-version>1.6.25.001</alpha-version> <alpha-version>1.6.26.001</alpha-version>
<sign.keystore>keystore</sign.keystore> <sign.keystore>keystore</sign.keystore>
<sign.alias>alias</sign.alias> <sign.alias>alias</sign.alias>
<sign.storepass>storepass</sign.storepass> <sign.storepass>storepass</sign.storepass>
@@ -19,7 +19,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.26-SNAPSHOT</version> <version>1.6.27-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-gui-android</artifactId> <artifactId>forge-gui-android</artifactId>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.26-SNAPSHOT</version> <version>1.6.27-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-gui-desktop</artifactId> <artifactId>forge-gui-desktop</artifactId>

View File

@@ -386,7 +386,7 @@ public enum FControl implements KeyEventDispatcher {
return true; return true;
} }
} }
else if (e.getID() == KeyEvent.KEY_PRESSED && e.getModifiers() == InputEvent.ALT_MASK) { else if (e.getID() == KeyEvent.KEY_PRESSED && e.getModifiersEx() == InputEvent.ALT_DOWN_MASK) {
altKeyLastDown = true; altKeyLastDown = true;
} }
} }

View File

@@ -20,7 +20,6 @@ package forge.gui;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.card.CardEdition; import forge.card.CardEdition;
import forge.card.CardRules;
import forge.item.IPaperCard; import forge.item.IPaperCard;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.model.FModel; import forge.model.FModel;
@@ -295,30 +294,10 @@ public class ImportSourceAnalyzer {
// character mangling on some system locales, but we want to replicate the old code here exactly // character mangling on some system locales, but we want to replicate the old code here exactly
return out.toString().toLowerCase(); return out.toString().toLowerCase();
} }
@Deprecated
private void addDefaultPicNames(final PaperCard c, final boolean backFace) { private void addDefaultPicNames(final PaperCard c, final boolean backFace) {
final CardRules card = c.getRules(); return;
final String urls = card.getPictureUrl(backFace);
if (StringUtils.isEmpty(urls)) { return; }
final int numPics = 1 + StringUtils.countMatches(urls, "\\");
if (c.getArtIndex() > numPics) {
return;
}
final String filenameBase = ImageUtil.getImageKey(c, backFace, false);
final String filename = filenameBase + ".jpg";
final boolean alreadyHadIt = null != defaultPicNames.put(filename, filename);
if ( alreadyHadIt ) {
return;
}
// Do you shift artIndex by one here?
final String newLastSymbol = 0 == c.getArtIndex() ? "" : String.valueOf(c.getArtIndex() /* + 1 */);
final String oldFilename = oldCleanString(filenameBase.replaceAll("[0-9]?(\\.full)?$", "")) + newLastSymbol + ".jpg";
//if ( numPics > 1 )
//System.out.printf("Will move %s -> %s%n", oldFilename, filename);
defaultPicOldNameToCurrentName.put(oldFilename, filename);
} }

View File

@@ -79,7 +79,7 @@ public class TextSearchFilter<T extends InventoryItem> extends ItemFilter<T> {
itemManager.focus(); itemManager.focus();
break; break;
case KeyEvent.VK_ENTER: case KeyEvent.VK_ENTER:
if (e.getModifiers() == 0) { if (e.getModifiersEx() == 0) {
if (changeTimer.isRunning()) { if (changeTimer.isRunning()) {
applyChange(); //apply change now if currently delayed applyChange(); //apply change now if currently delayed
} }

View File

@@ -327,7 +327,7 @@ public abstract class ItemView<T extends InventoryItem> {
private boolean popupShowing = false; private boolean popupShowing = false;
private Popup popup; private Popup popup;
private Timer popupTimer; private Timer popupTimer;
private static final int okModifiers = InputEvent.SHIFT_MASK | InputEvent.ALT_GRAPH_MASK; private static final int okModifiers = InputEvent.SHIFT_DOWN_MASK | InputEvent.ALT_GRAPH_DOWN_MASK;
public IncrementalSearch() { public IncrementalSearch() {
} }
@@ -481,7 +481,7 @@ public abstract class ItemView<T extends InventoryItem> {
//$FALL-THROUGH$ //$FALL-THROUGH$
default: default:
// shift and/or alt-graph down is ok. anything else is a hotkey (e.g. ctrl-f) // shift and/or alt-graph down is ok. anything else is a hotkey (e.g. ctrl-f)
if (okModifiers != (e.getModifiers() | okModifiers) if (okModifiers != (e.getModifiersEx() | okModifiers)
|| !CharUtils.isAsciiPrintable(e.getKeyChar())) { // escape sneaks in here on Windows || !CharUtils.isAsciiPrintable(e.getKeyChar())) { // escape sneaks in here on Windows
return; return;
} }

View File

@@ -585,7 +585,7 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
} }
public void addMoveItems(final String verb, final String dest) { public void addMoveItems(final String verb, final String dest) {
addItems(verb, dest, false, 0, InputEvent.SHIFT_DOWN_MASK, InputEvent.ALT_MASK); addItems(verb, dest, false, 0, InputEvent.SHIFT_DOWN_MASK, InputEvent.ALT_DOWN_MASK);
} }
public void addMoveAlternateItems(final String verb, final String dest) { public void addMoveAlternateItems(final String verb, final String dest) {
@@ -596,7 +596,7 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
addItems(verb, dest, true, InputEvent.CTRL_DOWN_MASK, addItems(verb, dest, true, InputEvent.CTRL_DOWN_MASK,
//getMenuShortcutKeyMask() instead of CTRL_DOWN_MASK since on OSX, ctrl-shift-space brings up the window manager //getMenuShortcutKeyMask() instead of CTRL_DOWN_MASK since on OSX, ctrl-shift-space brings up the window manager
InputEvent.SHIFT_DOWN_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), InputEvent.SHIFT_DOWN_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(),
InputEvent.ALT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); InputEvent.ALT_DOWN_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
} }
} }
} }

View File

@@ -42,7 +42,7 @@ public enum VCardScript implements IVDoc<CCardScript> {
txtScript.setDocument(doc); txtScript.setDocument(doc);
error = doc.addStyle("error", null); error = doc.addStyle("error", null);
error.addAttribute(StyleConstants.Background, Color.red); error.addAttribute(StyleConstants.Background, Color.red);
error.addAttribute(StyleConstants.Bold, new Boolean(true)); error.addAttribute(StyleConstants.Bold, Boolean.valueOf(true));
} }
public JTextPane getTxtScript() { public JTextPane getTxtScript() {

View File

@@ -200,8 +200,8 @@ public class FUndoManager extends UndoManager implements DocumentListener {
public UndoAction() { public UndoAction() {
putValue(Action.NAME, "Undo"); putValue(Action.NAME, "Undo");
putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME)); putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_U)); putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_U));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK)); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK));
setEnabled(false); setEnabled(false);
} }
@@ -229,8 +229,8 @@ public class FUndoManager extends UndoManager implements DocumentListener {
public RedoAction() { public RedoAction() {
putValue(Action.NAME, "Redo"); putValue(Action.NAME, "Redo");
putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME)); putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R)); putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK)); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK));
setEnabled(false); setEnabled(false);
} }

View File

@@ -1849,7 +1849,7 @@ public class GameSimulatorTest extends SimulationTestCase {
Card simSpark = (Card)sim.getGameCopier().find(sparkDouble); Card simSpark = (Card)sim.getGameCopier().find(sparkDouble);
assertTrue(simSpark != null); assertNotNull(simSpark);
assertTrue(simSpark.getZone().is(ZoneType.Battlefield)); assertTrue(simSpark.getZone().is(ZoneType.Battlefield));
assertTrue(simSpark.getCounters(CounterType.P1P1) == 1); assertTrue(simSpark.getCounters(CounterType.P1P1) == 1);
assertTrue(simSpark.getCounters(CounterType.LOYALTY) == 5); assertTrue(simSpark.getCounters(CounterType.LOYALTY) == 5);
@@ -1882,13 +1882,59 @@ public class GameSimulatorTest extends SimulationTestCase {
Card awakened = findCardWithName(sim.getSimulatedGameState(), "Vitu-Ghazi"); Card awakened = findCardWithName(sim.getSimulatedGameState(), "Vitu-Ghazi");
assertTrue(awakened != null); assertNotNull(awakened);
assertTrue(awakened.getName().equals("Vitu-Ghazi")); assertTrue(awakened.getName().equals("Vitu-Ghazi"));
assertTrue(awakened.getCounters(CounterType.P1P1) == 9); assertTrue(awakened.getCounters(CounterType.P1P1) == 9);
assertTrue(awakened.hasKeyword(Keyword.HASTE)); assertTrue(awakened.hasKeyword(Keyword.HASTE));
assertTrue(awakened.getType().hasSubtype("Goblin")); assertTrue(awakened.getType().hasSubtype("Goblin"));
} }
public void testNecroticOozeActivateOnce() {
Game game = initAndCreateGame();
Player p = game.getPlayers().get(0);
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p);
for (int i=0; i<7; i++) { addCardToZone("Swamp", p, ZoneType.Battlefield); }
for (int i=0; i<7; i++) { addCardToZone("Forest", p, ZoneType.Battlefield); }
addCardToZone("Basking Rootwalla", p, ZoneType.Graveyard);
Card ooze = addCardToZone("Necrotic Ooze", p, ZoneType.Hand);
SpellAbility oozeSA = ooze.getFirstSpellAbility();
GameSimulator sim = createSimulator(game, p);
sim.simulateSpellAbility(oozeSA);
Card oozeOTB = findCardWithName(sim.getSimulatedGameState(), "Necrotic Ooze");
assertNotNull(oozeOTB);
SpellAbility copiedSA = findSAWithPrefix(oozeOTB, "{1}{G}:");
assertNotNull(copiedSA);
assertTrue(copiedSA.getRestrictions().getLimitToCheck().equals("1"));
}
public void testEpochrasite() {
Game game = initAndCreateGame();
Player p = game.getPlayers().get(0);
game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p);
for (int i=0; i<7; i++) { addCardToZone("Swamp", p, ZoneType.Battlefield); }
Card epo = addCardToZone("Epochrasite", p, ZoneType.Graveyard);
Card animate = addCardToZone("Animate Dead", p, ZoneType.Hand);
SpellAbility saAnimate = animate.getFirstSpellAbility();
saAnimate.getTargets().add(epo);
GameSimulator sim = createSimulator(game, p);
sim.simulateSpellAbility(saAnimate);
Card epoOTB = findCardWithName(sim.getSimulatedGameState(), "Epochrasite");
assertNotNull(epoOTB);
assertTrue(epoOTB.getCounters(CounterType.P1P1) == 3);
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void broken_testCloneDimir() { public void broken_testCloneDimir() {
Game game = initAndCreateGame(); Game game = initAndCreateGame();

View File

@@ -6,13 +6,13 @@
<packaging.type>jar</packaging.type> <packaging.type>jar</packaging.type>
<build.min.memory>-Xms128m</build.min.memory> <build.min.memory>-Xms128m</build.min.memory>
<build.max.memory>-Xmx2048m</build.max.memory> <build.max.memory>-Xmx2048m</build.max.memory>
<alpha-version>1.6.25.001</alpha-version> <alpha-version>1.6.26.001</alpha-version>
</properties> </properties>
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.26-SNAPSHOT</version> <version>1.6.27-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-gui-ios</artifactId> <artifactId>forge-gui-ios</artifactId>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.26-SNAPSHOT</version> <version>1.6.27-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-gui-mobile-dev</artifactId> <artifactId>forge-gui-mobile-dev</artifactId>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.26-SNAPSHOT</version> <version>1.6.27-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-gui-mobile</artifactId> <artifactId>forge-gui-mobile</artifactId>

View File

@@ -34,7 +34,7 @@ import java.util.List;
import java.util.Stack; import java.util.Stack;
public class Forge implements ApplicationListener { public class Forge implements ApplicationListener {
public static final String CURRENT_VERSION = "1.6.25.001"; public static final String CURRENT_VERSION = "1.6.26.001";
private static final ApplicationListener app = new Forge(); private static final ApplicationListener app = new Forge();
private static Clipboard clipboard; private static Clipboard clipboard;

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.26-SNAPSHOT</version> <version>1.6.27-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-gui</artifactId> <artifactId>forge-gui</artifactId>

View File

@@ -1,5 +1,2 @@
- New Net Deck Category -
The Genetic Algorithm AI Decks category has been added to net decks. This contains decks generated using a genetic algorithm to try to find decks that the AI plays best in each format.
- Bug fixes - - Bug fixes -
As always, this release of Forge features an assortment of bug fixes and improvements based on user feedback during the previous release run. As always, this release of Forge features an assortment of bug fixes and improvements based on user feedback during the previous release run.

View File

@@ -2,9 +2,9 @@ Name:Ajani's Pridemate
ManaCost:1 W ManaCost:1 W
Types:Creature Cat Soldier Types:Creature Cat Soldier
PT:2/2 PT:2/2
T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | OptionalDecider$ You | TriggerDescription$ Whenever you gain life, you may put a +1/+1 counter on CARDNAME. T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you gain life, put a +1/+1 counter on CARDNAME.
SVar:TrigPutCounter:DB$PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 SVar:TrigPutCounter:DB$PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
SVar:Picture:http://www.wizards.com/global/images/magic/general/ajanis_pridemate.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/ajanis_pridemate.jpg
DeckHints:Ability$LifeGain DeckHints:Ability$LifeGain
DeckHas:Ability$Counters DeckHas:Ability$Counters
Oracle:Whenever you gain life, you may put a +1/+1 counter on Ajani's Pridemate. Oracle:Whenever you gain life, put a +1/+1 counter on Ajani's Pridemate.

View File

@@ -3,7 +3,7 @@ ManaCost:6 W
Types:Sorcery Types:Sorcery
T:Mode$ SpellCast | ValidCard$ Card.Self | Static$ True | Execute$ ApproachingSuns T:Mode$ SpellCast | ValidCard$ Card.Self | Static$ True | Execute$ ApproachingSuns
SVar:ApproachingSuns:DB$ Effect | Name$ ApproachingSuns | Duration$ Permanent SVar:ApproachingSuns:DB$ Effect | Name$ ApproachingSuns | Duration$ Permanent
A:SP$ Branch | Cost$ 6 W | References$ X,Y,Z | BranchConditionSVar$ X | BranchConditionSVarCompare$ EQ3 | TrueSubAbility$ WinGame | FalseSubAbility$ GainLife | SpellDescription$ If CARDNAME was cast from your hand and you've cast another spell named Approach of the Second Sun this game, you win the game. Otherwise, put CARDNAME into its owner's library seventh from the top and you gain 7 life. A:SP$ Branch | Cost$ 6 W | References$ X,Y,Z | BranchConditionSVar$ X | BranchConditionSVarCompare$ EQ3 | TrueSubAbility$ WinGame | FalseSubAbility$ GainLife | SpellDescription$ If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, you win the game. Otherwise, put CARDNAME into its owner's library seventh from the top and you gain 7 life.
SVar:WinGame:DB$ WinsGame | Defined$ You SVar:WinGame:DB$ WinsGame | Defined$ You
SVar:GainLife:DB$ GainLife | LifeAmount$ 7 | Defined$ You | SubAbility$ Reapproach SVar:GainLife:DB$ GainLife | LifeAmount$ 7 | Defined$ You | SubAbility$ Reapproach
SVar:Reapproach:DB$ ChangeZone | Origin$ Stack | Destination$ Library | LibraryPosition$ 6 | Defined$ Self SVar:Reapproach:DB$ ChangeZone | Origin$ Stack | Destination$ Library | LibraryPosition$ 6 | Defined$ Self
@@ -11,4 +11,4 @@ SVar:Y:Count$ValidCommand Effect.YouCtrl+namedApproachingSuns/LimitMax.2
SVar:Z:Count$ValidStack Card.wasCastFromHand+Self/LimitMax.1 SVar:Z:Count$ValidStack Card.wasCastFromHand+Self/LimitMax.1
SVar:X:SVar$Y/Plus.Z SVar:X:SVar$Y/Plus.Z
DeckNeeds:Name$Approach of the Second Sun DeckNeeds:Name$Approach of the Second Sun
Oracle:If Approach of the Second Sun was cast from your hand and you've cast another spell named Approach of the Second Sun this game, you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top and you gain 7 life. Oracle:If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top and you gain 7 life.

View File

@@ -2,7 +2,7 @@ Name:Artisan of Forms
ManaCost:1 U ManaCost:1 U
Types:Creature Human Wizard Types:Creature Human Wizard
PT:1/1 PT:1/1
T:Mode$ SpellCast | ValidActivatingPlayer$ You | TargetsValid$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigArtisanCopy | TriggerDescription$ Heroic — Whenever you cast a spell that targets CARDNAME, you may have CARDNAME become a copy of target creature and gain this ability. T:Mode$ SpellCast | ValidActivatingPlayer$ You | TargetsValid$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigArtisanCopy | TriggerDescription$ Heroic — Whenever you cast a spell that targets CARDNAME, you may have CARDNAME become a copy of target creature, except it has this ability.
SVar:TrigArtisanCopy:DB$ Clone | ValidTgts$ Creature | TgtPrompt$ Select target creature to copy | Optional$ True | GainThisAbility$ True | AddSVars$ TrigArtisanCopy | AILogic$ CloneBestCreature SVar:TrigArtisanCopy:DB$ Clone | ValidTgts$ Creature | TgtPrompt$ Select target creature to copy | Optional$ True | GainThisAbility$ True | AddSVars$ TrigArtisanCopy | AILogic$ CloneBestCreature
SVar:Picture:http://www.wizards.com/global/images/magic/general/artisan_of_forms.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/artisan_of_forms.jpg
Oracle:Heroic — Whenever you cast a spell that targets Artisan of Forms, you may have Artisan of Forms become a copy of target creature and gain this ability. Oracle:Heroic — Whenever you cast a spell that targets Artisan of Forms, you may have Artisan of Forms become a copy of target creature, except it has this ability.

View File

@@ -6,5 +6,6 @@ K:Kicker:1 G
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+kicked | Execute$ TrigKicker | TriggerDescription$ When CARDNAME enters the battlefield, if it was kicked, destroy target land. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+kicked | Execute$ TrigKicker | TriggerDescription$ When CARDNAME enters the battlefield, if it was kicked, destroy target land.
SVar:TrigKicker:DB$Destroy | ValidTgts$ Land | TgtPrompt$ Select target land SVar:TrigKicker:DB$Destroy | ValidTgts$ Land | TgtPrompt$ Select target land
DeckHints:Color$Green DeckHints:Color$Green
SVar:NeedsToPlayKicked:Land.OppCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/benalish_emissary.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/benalish_emissary.jpg
Oracle:Kicker {1}{G} (You may pay an additional {1}{G} as you cast this spell.)\nWhen Benalish Emissary enters the battlefield, if it was kicked, destroy target land. Oracle:Kicker {1}{G} (You may pay an additional {1}{G} as you cast this spell.)\nWhen Benalish Emissary enters the battlefield, if it was kicked, destroy target land.

View File

@@ -4,5 +4,8 @@ Types:Sorcery
K:Kicker:Sac<2/Land> K:Kicker:Sac<2/Land>
A:SP$ Discard | Cost$ 2 B | ValidTgts$ Player | TgtPrompt$ Choose a player | NumCards$ WasKicked | References$ WasKicked | Mode$ TgtChoose | SpellDescription$ Target player discards two cards. If CARDNAME was kicked, that player discards three cards instead. A:SP$ Discard | Cost$ 2 B | ValidTgts$ Player | TgtPrompt$ Choose a player | NumCards$ WasKicked | References$ WasKicked | Mode$ TgtChoose | SpellDescription$ Target player discards two cards. If CARDNAME was kicked, that player discards three cards instead.
SVar:WasKicked:Count$Kicked.3.2 SVar:WasKicked:Count$Kicked.3.2
SVar:NeedsToPlayKickedVar:Z GE3
SVar:Z:Count$ValidHand Card.OppCtrl
SVar:AIPreference:SacCost$Land.basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/bog_down.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/bog_down.jpg
Oracle:Kicker—Sacrifice two lands. (You may sacrifice two lands in addition to any other costs as you cast this spell.)\nTarget player discards two cards. If this spell was kicked, that player discards three cards instead. Oracle:Kicker—Sacrifice two lands. (You may sacrifice two lands in addition to any other costs as you cast this spell.)\nTarget player discards two cards. If this spell was kicked, that player discards three cards instead.

View File

@@ -5,4 +5,6 @@ PT:1/3
K:Kicker:3 B K:Kicker:3 B
T:Mode$ ChangesZone | ValidCard$ Card.Self+kicked | Origin$ Any | Destination$ Battlefield | Execute$ TrigDiscard | TriggerDescription$ When CARDNAME enters the battlefield, if it was kicked, each opponent discards two cards. T:Mode$ ChangesZone | ValidCard$ Card.Self+kicked | Origin$ Any | Destination$ Battlefield | Execute$ TrigDiscard | TriggerDescription$ When CARDNAME enters the battlefield, if it was kicked, each opponent discards two cards.
SVar:TrigDiscard:DB$ Discard | Defined$ Player.Opponent | NumCards$ 2 | Mode$ TgtChoose SVar:TrigDiscard:DB$ Discard | Defined$ Player.Opponent | NumCards$ 2 | Mode$ TgtChoose
SVar:NeedsToPlayKickedVar:Z GE1
SVar:Z:Count$ValidHand Card.OppCtrl
Oracle:Kicker {3}{B} (You may pay an additional {3}{B} as you cast this spell.)\nWhen Caligo Skin-Witch enters the battlefield, if it was kicked, each opponent discards two cards. Oracle:Kicker {3}{B} (You may pay an additional {3}{B} as you cast this spell.)\nWhen Caligo Skin-Witch enters the battlefield, if it was kicked, each opponent discards two cards.

View File

@@ -3,8 +3,8 @@ ManaCost:1 UB UB
Types:Creature Shapeshifter Types:Creature Shapeshifter
PT:1/2 PT:1/2
# Make Svars for granting abilities and triggers on clones distinct to avoid SVars getting overwritten when cloning a clone # Make Svars for granting abilities and triggers on clones distinct to avoid SVars getting overwritten when cloning a clone
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature | TriggerZones$ Battlefield | Execute$ CemeteryPucaCopy | TriggerDescription$ Whenever a creature dies, you may pay {1}. If you do, CARDNAME becomes a copy of that creature and gains this ability. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature | TriggerZones$ Battlefield | Execute$ CemeteryPucaCopy | TriggerDescription$ Whenever a creature dies, you may pay {1}. If you do, CARDNAME becomes a copy of that creature, except it has this ability.
SVar:CemeteryPucaCopy:AB$ Clone | Cost$ 1 | Defined$ TriggeredCardLKICopy | GainThisAbility$ True SVar:CemeteryPucaCopy:AB$ Clone | Cost$ 1 | Defined$ TriggeredCardLKICopy | GainThisAbility$ True
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/cemetery_puca.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/cemetery_puca.jpg
Oracle:Whenever a creature dies, you may pay {1}. If you do, Cemetery Puca becomes a copy of that creature and gains this ability. Oracle:Whenever a creature dies, you may pay {1}. If you do, Cemetery Puca becomes a copy of that creature, except it has this ability.

View File

@@ -3,8 +3,8 @@ ManaCost:2 W
Types:Creature Human Soldier Types:Creature Human Soldier
PT:1/2 PT:1/2
K:Protection from black K:Protection from black
S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ All | AddKeyword$ Alternative Cost:0 | CheckSVar$ X | CheckSecondSVar$ Y | Description$ If an opponent controls a Swamp and you control a Plains, you may cast CARDNAME without paying its mana cost. S:Mode$ Continuous | Affected$ Card.Self | EffectZone$ All | AddKeyword$ Alternative Cost:0 | CheckSVar$ X | CheckSecondSVar$ Y | Description$ If an opponent controls a Swamp and you control a Plains, you may cast this spell without paying its mana cost.
SVar:X:Count$Valid Swamp.OppCtrl SVar:X:Count$Valid Swamp.OppCtrl
SVar:Y:Count$Valid Plains.YouCtrl SVar:Y:Count$Valid Plains.YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/cho_arrim_legate.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/cho_arrim_legate.jpg
Oracle:Protection from black\nIf an opponent controls a Swamp and you control a Plains, you may cast Cho-Arrim Legate without paying its mana cost. Oracle:Protection from black\nIf an opponent controls a Swamp and you control a Plains, you may cast this spell without paying its mana cost.

Some files were not shown because too many files have changed in this diff Show More