package forge.card.abilityFactory;
import forge.*;
import forge.card.cardFactory.CardFactoryUtil;
import forge.card.spellability.*;
import forge.gui.GuiUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
/**
* <p>AbilityFactory_ChangeZone class.</p>
*
* @author Forge
* @version $Id: $
*/
public class AbilityFactory_ChangeZone {
// Change Zone is going to work much differently than other AFs.
// *NOTE* Please do not use this as a base for copying and creating your own AF
/**
* <p>createAbilityChangeZone.</p>
*
* @param AF a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityChangeZone(final AbilityFactory AF) {
final SpellAbility abChangeZone = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) {
private static final long serialVersionUID = 3728332812890211671L;
@Override
public boolean canPlayAI() {
return changeZoneCanPlayAI(AF, this);
}
@Override
public void resolve() {
changeZoneResolve(AF, this);
}
@Override
public String getStackDescription() {
return changeZoneDescription(AF, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return changeZoneTriggerAI(AF, this, mandatory);
}
};
setMiscellaneous(AF, abChangeZone);
return abChangeZone;
}
/**
* <p>createSpellChangeZone.</p>
*
* @param AF a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellChangeZone(final AbilityFactory AF) {
final SpellAbility spChangeZone = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) {
private static final long serialVersionUID = 3270484211099902059L;
@Override
public boolean canPlayAI() {
return changeZoneCanPlayAI(AF, this);
}
@Override
public void resolve() {
changeZoneResolve(AF, this);
}
@Override
public String getStackDescription() {
return changeZoneDescription(AF, this);
}
};
setMiscellaneous(AF, spChangeZone);
return spChangeZone;
}
/**
* <p>createDrawbackChangeZone.</p>
*
* @param AF a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createDrawbackChangeZone(final AbilityFactory AF) {
final SpellAbility dbChangeZone = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) {
private static final long serialVersionUID = 3270484211099902059L;
@Override
public void resolve() {
changeZoneResolve(AF, this);
}
@Override
public boolean chkAI_Drawback() {
return changeZonePlayDrawbackAI(AF, this);
}
@Override
public String getStackDescription() {
return changeZoneDescription(AF, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return changeZoneTriggerAI(AF, this, mandatory);
}
};
setMiscellaneous(AF, dbChangeZone);
return dbChangeZone;
}
/**
* <p>isHidden.</p>
*
* @param origin a {@link java.lang.String} object.
* @param hiddenOverride a boolean.
* @return a boolean.
*/
public static boolean isHidden(String origin, boolean hiddenOverride) {
return (hiddenOverride || origin.equals("Library") || origin.equals("Hand") || origin.equals("Sideboard"));
}
/**
* <p>isKnown.</p>
*
* @param origin a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean isKnown(String origin) {
return (origin.equals("Graveyard") || origin.equals("Exile") || origin.equals("Battlefield") || origin.equals("Stack"));
}
/**
* <p>setMiscellaneous.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void setMiscellaneous(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
String origin = params.get("Origin");
Target tgt = af.getAbTgt();
// Don't set the zone if it targets a player
if (tgt != null && !tgt.canTgtPlayer())
af.getAbTgt().setZone(origin);
if (!(sa instanceof Ability_Sub))
if (origin.equals("Battlefield") || params.get("Destination").equals("Battlefield"))
af.getHostCard().setSVar("PlayMain1", "TRUE");
}
/**
* <p>changeZoneCanPlayAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean.
*/
private static boolean changeZoneCanPlayAI(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
String origin = params.get("Origin");
if (isHidden(origin, params.containsKey("Hidden")))
return changeHiddenOriginCanPlayAI(af, sa);
else if (isKnown(origin))
return changeKnownOriginCanPlayAI(af, sa);
return false;
}
/**
* <p>changeZonePlayDrawbackAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean.
*/
private static boolean changeZonePlayDrawbackAI(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
String origin = params.get("Origin");
if (isHidden(origin, params.containsKey("Hidden")))
return changeHiddenOriginPlayDrawbackAI(af, sa);
else if (isKnown(origin))
return changeKnownOriginPlayDrawbackAI(af, sa);
return false;
}
/**
* <p>changeZoneTriggerAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param mandatory a boolean.
* @return a boolean.
*/
private static boolean changeZoneTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory) {
HashMap<String, String> params = af.getMapParams();
String origin = params.get("Origin");
if (isHidden(origin, params.containsKey("Hidden")))
return changeHiddenTriggerAI(af, sa, mandatory);
else if (isKnown(origin))
return changeKnownOriginTriggerAI(af, sa, mandatory);
return false;
}
/**
* <p>changeZoneDescription.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a {@link java.lang.String} object.
*/
private static String changeZoneDescription(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
String origin = params.get("Origin");
if (isHidden(origin, params.containsKey("Hidden")))
return changeHiddenOriginStackDescription(af, sa);
else if (isKnown(origin))
return changeKnownOriginStackDescription(af, sa);
return "";
}
/**
* <p>changeZoneResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void changeZoneResolve(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
String origin = params.get("Origin");
if (isHidden(origin, params.containsKey("Hidden")) && !params.containsKey("Ninjutsu"))
changeHiddenOriginResolve(af, sa);
else if (isKnown(origin) || params.containsKey("Ninjutsu"))
changeKnownOriginResolve(af, sa);
}
// *************************************************************************************
// ************ Hidden Origin (Library/Hand/Sideboard/Non-targetd other) ***************
// ******* Hidden origin cards are chosen on the resolution of the spell ***************
// ******* It is possible for these to have Destination of Battlefield *****************
// ****** Example: Cavern Harpy where you don't choose the card until resolution *******
// *************************************************************************************
/**
* <p>changeHiddenOriginCanPlayAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean.
*/
private static boolean changeHiddenOriginCanPlayAI(AbilityFactory af, SpellAbility sa) {
// Fetching should occur fairly often as it helps cast more spells, and have access to more mana
Cost abCost = af.getAbCost();
Card source = af.getHostCard();
HashMap<String, String> params = af.getMapParams();
String origin = params.get("Origin");
//String destination = params.get("Destination");
if (abCost != null) {
// AI currently disabled for these costs
if (abCost.getSacCost() && !abCost.getSacThis()) {
//only sacrifice something that's supposed to be sacrificed
String type = abCost.getSacType();
CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
typeList = typeList.getValidCards(type.split(","), source.getController(), source);
if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null)
return false;
}
if (abCost.getLifeCost()) {
if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4)
return false;
}
if (abCost.getDiscardCost()) return false;
if (abCost.getSubCounter()) ; // SubCounter is fine
}
if (!ComputerUtil.canPayCost(sa))
return false;
Random r = MyRandom.random;
// prevent run-away activations - first time will always return true
boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());
ArrayList<Player> pDefined;
Target tgt = af.getAbTgt();
if (tgt != null && tgt.canTgtPlayer()) {
if (af.isCurse())
tgt.addTarget(AllZone.getHumanPlayer());
else
tgt.addTarget(AllZone.getComputerPlayer());
pDefined = tgt.getTargetPlayers();
} else {
pDefined = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa);
}
String type = params.get("ChangeType");
if (type != null) {
if (type.contains("X") && source.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
int xPay = ComputerUtil.determineLeftoverMana(sa);
source.setSVar("PayX", Integer.toString(xPay));
}
}
for (Player p : pDefined) {
CardList list = AllZoneUtil.getCardsInZone(origin, p);
if (type != null && p.isComputer()) {
// AI only "knows" about his information
list = AbilityFactory.filterListByType(list, params.get("ChangeType"), sa);
}
if (list.isEmpty())
return false;
}
chance &= (r.nextFloat() < .8);
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
chance &= subAb.chkAI_Drawback();
return chance;
}
/**
* <p>changeHiddenOriginPlayDrawbackAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean.
*/
private static boolean changeHiddenOriginPlayDrawbackAI(AbilityFactory af, SpellAbility sa) {
// if putting cards from hand to library and parent is drawing cards
// make sure this will actually do something:
Target tgt = af.getAbTgt();
if (tgt != null && tgt.canTgtPlayer()) {
if (af.isCurse())
tgt.addTarget(AllZone.getHumanPlayer());
else
tgt.addTarget(AllZone.getComputerPlayer());
}
return true;
}
/**
* <p>changeHiddenTriggerAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param mandatory a boolean.
* @return a boolean.
*/
private static boolean changeHiddenTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory) {
// Fetching should occur fairly often as it helps cast more spells, and have access to more mana
if (!ComputerUtil.canPayCost(sa))
return false;
Card source = sa.getSourceCard();
HashMap<String, String> params = af.getMapParams();
//String destination = params.get("Destination");
String origin = params.get("Origin");
// this works for hidden because the mana is paid first.
String type = params.get("ChangeType");
if (type != null && type.contains("X") && source.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
int xPay = ComputerUtil.determineLeftoverMana(sa);
source.setSVar("PayX", Integer.toString(xPay));
}
ArrayList<Player> pDefined;
Target tgt = af.getAbTgt();
if (tgt != null && tgt.canTgtPlayer()) {
if (af.isCurse()) {
if (AllZone.getHumanPlayer().canTarget(source))
tgt.addTarget(AllZone.getHumanPlayer());
else if (mandatory && AllZone.getComputerPlayer().canTarget(source))
tgt.addTarget(AllZone.getComputerPlayer());
} else {
if (AllZone.getComputerPlayer().canTarget(source))
tgt.addTarget(AllZone.getComputerPlayer());
else if (mandatory && AllZone.getHumanPlayer().canTarget(source))
tgt.addTarget(AllZone.getHumanPlayer());
}
pDefined = tgt.getTargetPlayers();
if (pDefined.isEmpty())
return false;
if (mandatory) {
return pDefined.size() > 0;
}
} else {
if (mandatory)
return true;
pDefined = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa);
}
for (Player p : pDefined) {
CardList list = AllZoneUtil.getCardsInZone(origin, p);
if (p.isComputer()) // Computer should "know" his deck
list = AbilityFactory.filterListByType(list, params.get("ChangeType"), sa);
if (list.isEmpty())
return false;
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
return subAb.doTrigger(mandatory);
return true;
}
/**
* <p>changeHiddenOriginStackDescription.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a {@link java.lang.String} object.
*/
private static String changeHiddenOriginStackDescription(AbilityFactory af, SpellAbility sa) {
// TODO: build Stack Description will need expansion as more cards are added
HashMap<String, String> params = af.getMapParams();
StringBuilder sb = new StringBuilder();
Card host = af.getHostCard();
if (!(sa instanceof Ability_Sub))
sb.append(host.getName()).append(" -");
sb.append(" ");
if (params.containsKey("StackDescription"))
sb.append(params.get("StackDescription"));
else {
String origin = params.get("Origin");
String destination = params.get("Destination");
String type = params.containsKey("ChangeType") ? params.get("ChangeType") : "Card";
int num = params.containsKey("ChangeNum") ? AbilityFactory.calculateAmount(host, params.get("ChangeNum"), sa) : 1;
if (origin.equals("Library")) {
sb.append("Search your library for ").append(num).append(" ").append(type).append(" and ");
if (params.get("ChangeNum").equals("1"))
sb.append("put that card ");
else
sb.append("put those cards ");
if (destination.equals("Battlefield")) {
sb.append("onto the battlefield");
if (params.containsKey("Tapped"))
sb.append(" tapped");
sb.append(".");
}
if (destination.equals("Hand"))
sb.append("into your hand.");
if (destination.equals("Graveyard"))
sb.append("into your graveyard.");
sb.append("Then shuffle your library.");
} else if (origin.equals("Hand")) {
sb.append("Put ").append(num).append(" ").append(type).append(" card(s) from your hand ");
if (destination.equals("Battlefield"))
sb.append("onto the battlefield.");
if (destination.equals("Library")) {
int libraryPos = params.containsKey("LibraryPosition") ? Integer.parseInt(params.get("LibraryPosition")) : 0;
if (libraryPos == 0)
sb.append("on top");
if (libraryPos == -1)
sb.append("on bottom");
sb.append(" of your library.");
}
} else if (origin.equals("Battlefield")) {
// TODO: Expand on this Description as more cards use it
// for the non-targeted SAs when you choose what is returned on resolution
sb.append("Return ").append(num).append(" ").append(type).append(" card(s) ");
sb.append(" to your ").append(destination);
}
}
Ability_Sub abSub = sa.getSubAbility();
if (abSub != null) {
sb.append(abSub.getStackDescription());
}
return sb.toString();
}
/**
* <p>changeHiddenOriginResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void changeHiddenOriginResolve(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
ArrayList<Player> fetchers;
fetchers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa);
Player chooser = null;
if (params.containsKey("Chooser")) {
String choose = params.get("Chooser");
if (choose.equals("Targeted") && af.getAbTgt().getTargetPlayers() != null)
chooser = af.getAbTgt().getTargetPlayers().get(0);
else
chooser = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), choose, sa).get(0);
}
for (Player player : fetchers) {
Player decider = chooser;
if (decider == null)
decider = player;
if (decider.isComputer()) {
changeHiddenOriginResolveAI(af, sa, player);
} else {
changeHiddenOriginResolveHuman(af, sa, player);
}
}
}
/**
* <p>changeHiddenOriginResolveHuman.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param player a {@link forge.Player} object.
*/
private static void changeHiddenOriginResolveHuman(AbilityFactory af, SpellAbility sa, Player player) {
HashMap<String, String> params = af.getMapParams();
Card card = sa.getSourceCard();
Target tgt = af.getAbTgt();
if (tgt != null) {
ArrayList<Player> players = tgt.getTargetPlayers();
player = players.get(0);
if (players.contains(player) && !player.canTarget(sa.getSourceCard()))
return;
}
String origin = params.get("Origin");
String destination = params.get("Destination");
// this needs to be zero indexed. Top = 0, Third = 2
int libraryPos = params.containsKey("LibraryPosition") ? Integer.parseInt(params.get("LibraryPosition")) : 0;
if (params.containsKey("OriginChoice")) {
// Currently only used for Mishra, but may be used by other things
// Improve how this message reacts for other cards
String alt = params.get("OriginAlternative");
CardList altFetchList = AllZoneUtil.getCardsInZone(alt, player);
altFetchList = AbilityFactory.filterListByType(altFetchList, params.get("ChangeType"), sa);
StringBuilder sb = new StringBuilder();
sb.append(params.get("AlternativeMessage")).append(" ");
sb.append(altFetchList.size()).append(" cards match your searching type in Alternate Zones.");
if (!GameActionUtil.showYesNoDialog(card, sb.toString()))
origin = alt;
}
if (params.containsKey("DestinationAlternative")) {
StringBuilder sb = new StringBuilder();
sb.append(params.get("AlternativeDestinationMessage"));
if (!GameActionUtil.showYesNoDialog(card, sb.toString())) {
destination = params.get("DestinationAlternative");
libraryPos = params.containsKey("LibraryPositionAlternative") ? Integer.parseInt(params.get("LibraryPositionAlternative")) : 0;
}
}
CardList fetchList = AllZoneUtil.getCardsInZone(origin, player);
if (origin.contains("Library")) // Look at whole library before moving onto choosing a card{
GuiUtils.getChoiceOptional(af.getHostCard().getName() + " - Looking at Library", AllZoneUtil.getCardsInZone("Library", player).toArray());
if (origin.contains("Hand") && player.isComputer()) // Look at opponents hand before moving onto choosing a card
GuiUtils.getChoiceOptional(af.getHostCard().getName() + " - Looking at Opponent's Hand", AllZoneUtil.getCardsInZone("Hand", player).toArray());
fetchList = AbilityFactory.filterListByType(fetchList, params.get("ChangeType"), sa);
PlayerZone destZone = AllZone.getZone(destination, player);
int changeNum = params.containsKey("ChangeNum") ? AbilityFactory.calculateAmount(card, params.get("ChangeNum"), sa) : 1;
String remember = params.get("RememberChanged");
for (int i = 0; i < changeNum; i++) {
if (fetchList.size() == 0 || destination == null)
break;
Object o;
if (params.containsKey("Mandatory"))
o = GuiUtils.getChoice("Select a card", fetchList.toArray());
else
o = GuiUtils.getChoiceOptional("Select a card", fetchList.toArray());
if (o != null) {
Card c = (Card) o;
fetchList.remove(c);
Card movedCard = null;
if (destination.equals("Library")) {
// do not shuffle the library once we have placed a fetched card on top.
if (origin.contains("Library") && i < 1) {
player.shuffle();
}
movedCard = AllZone.getGameAction().moveToLibrary(c, libraryPos);
} else if (destination.equals("Battlefield")) {
if (params.containsKey("Tapped"))
c.tap();
if (params.containsKey("GainControl"))
c.setController(sa.getActivatingPlayer());
movedCard = AllZone.getGameAction().moveTo(AllZone.getZone(destination, c.getController()), c);
} else
movedCard = AllZone.getGameAction().moveTo(destZone, c);
if (remember != null)
card.addRemembered(movedCard);
//for imprinted since this doesn't use Target
if (params.containsKey("Imprint"))
card.addImprinted(movedCard);
} else {
StringBuilder sb = new StringBuilder();
int num = Math.min(fetchList.size(), changeNum - i);
sb.append("Cancel Search? Up to ").append(num).append(" more cards can change zones.");
if (i + 1 == changeNum || GameActionUtil.showYesNoDialog(card, sb.toString()))
break;
}
}
if ((origin.contains("Library") && !destination.equals("Library")) || params.containsKey("Shuffle"))
player.shuffle();
}
/**
* <p>changeHiddenOriginResolveAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param player a {@link forge.Player} object.
*/
private static void changeHiddenOriginResolveAI(AbilityFactory af, SpellAbility sa, Player player) {
HashMap<String, String> params = af.getMapParams();
Target tgt = af.getAbTgt();
Card card = af.getHostCard();
if (tgt != null) {
if (!tgt.getTargetPlayers().isEmpty()) {
player = tgt.getTargetPlayers().get(0);
if (!player.canTarget(sa.getSourceCard()))
return;
}
}
String origin = params.get("Origin");
CardList fetchList = AllZoneUtil.getCardsInZone(origin, player);
fetchList = AbilityFactory.filterListByType(fetchList, params.get("ChangeType"), sa);
String destination = params.get("Destination");
PlayerZone destZone = AllZone.getZone(destination, player);
String type = params.get("ChangeType");
if (type == null)
type = "Card";
CardList fetched = new CardList();
int changeNum = params.containsKey("ChangeNum") ? AbilityFactory.calculateAmount(card, params.get("ChangeNum"), sa) : 1;
String remember = params.get("RememberChanged");
for (int i = 0; i < changeNum; i++) {
if (fetchList.size() == 0 || destination == null)
break;
// Improve the AI for fetching.
Card c;
if (type.contains("Basic"))
c = basicManaFixing(fetchList);
else if (areAllBasics(type)) // if Searching for only basics,
c = basicManaFixing(fetchList, type);
else if (fetchList.getNotType("Creature").size() == 0)
c = CardFactoryUtil.AI_getBestCreature(fetchList); //if only creatures take the best
else if ("Battlefield".equals(destination) || "Graveyard".equals(destination))
c = CardFactoryUtil.AI_getMostExpensivePermanent(fetchList, af.getHostCard(), false);
else if ("Exile".equals(destination)) {
// Exiling your own stuff, if Exiling opponents stuff choose best
if (destZone.getPlayer().isHuman())
c = CardFactoryUtil.AI_getMostExpensivePermanent(fetchList, af.getHostCard(), false);
else
c = CardFactoryUtil.AI_getCheapestPermanent(fetchList, af.getHostCard(), false);
} else {
//Don't fetch another tutor with the same name
if (origin.contains("Library") && !fetchList.getNotName(card.getName()).isEmpty())
fetchList = fetchList.getNotName(card.getName());
fetchList.shuffle();
c = fetchList.get(0);
}
fetched.add(c);
fetchList.remove(c);
}
if (origin.contains("Library"))
player.shuffle();
for (Card c : fetched) {
Card newCard = null;
if ("Library".equals(destination)) {
int libraryPos = params.containsKey("LibraryPosition") ? Integer.parseInt(params.get("LibraryPosition")) : 0;
AllZone.getGameAction().moveToLibrary(c, libraryPos);
} else if ("Battlefield".equals(destination)) {
if (params.containsKey("Tapped"))
c.tap();
if (params.containsKey("GainControl"))
c.setController(sa.getActivatingPlayer());
newCard = AllZone.getGameAction().moveTo(AllZone.getZone(destination, c.getController()), c);
} else
newCard = AllZone.getGameAction().moveTo(destZone, c);
if (remember != null)
card.addRemembered(newCard);
//for imprinted since this doesn't use Target
if (params.containsKey("Imprint")) card.addImprinted(newCard);
}
if (!"Battlefield".equals(destination) && !"Card".equals(type)) {
String picked = af.getHostCard().getName() + " - Computer picked:";
if (fetched.size() > 0)
GuiUtils.getChoice(picked, fetched.toArray());
else
GuiUtils.getChoice(picked, new String[]{"<Nothing>"});
}
}
// *********** Utility functions for Hidden ********************
/**
* <p>basicManaFixing.</p>
*
* @param list a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
*/
private static Card basicManaFixing(CardList list) { // Search for a Basic Land
return basicManaFixing(list, "Plains, Island, Swamp, Mountain, Forest");
}
/**
* <p>basicManaFixing.</p>
*
* @param list a {@link forge.CardList} object.
* @param type a {@link java.lang.String} object.
* @return a {@link forge.Card} object.
*/
private static Card basicManaFixing(CardList list, String type) { // type = basic land types
CardList combined = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
combined.addAll(AllZoneUtil.getPlayerHand(AllZone.getComputerPlayer()));
String names[] = type.split(",");
ArrayList<String> basics = new ArrayList<String>();
// what types can I go get?
for (int i = 0; i < names.length; i++) {
if (list.getType(names[i]).size() != 0)
basics.add(names[i]);
}
// Which basic land is least available from hand and play, that I still have in my deck
int minSize = Integer.MAX_VALUE;
String minType = null;
for (int i = 0; i < basics.size(); i++) {
String b = basics.get(i);
int num = combined.getType(names[i]).size();
if (num < minSize) {
minType = b;
minSize = num;
}
}
if (minType != null)
list = list.getType(minType);
return list.get(0);
}
/**
* <p>areAllBasics.</p>
*
* @param types a {@link java.lang.String} object.
* @return a boolean.
*/
private static boolean areAllBasics(String types) {
String[] split = types.split(",");
String names[] = {"Plains", "Island", "Swamp", "Mountain", "Forest"};
boolean[] bBasic = new boolean[split.length];
for (String s : names) {
for (int i = 0; i < split.length; i++)
bBasic[i] |= s.equals(split[i]);
}
for (int i = 0; i < split.length; i++)
if (!bBasic[i])
return false;
return true;
}
// *************************************************************************************
// **************** Known Origin (Battlefield/Graveyard/Exile) *************************
// ******* Known origin cards are chosen during casting of the spell (target) **********
// *************************************************************************************
/**
* <p>changeKnownOriginCanPlayAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean.
*/
private static boolean changeKnownOriginCanPlayAI(AbilityFactory af, SpellAbility sa) {
// Retrieve either this card, or target Cards in Graveyard
Cost abCost = af.getAbCost();
final Card source = af.getHostCard();
HashMap<String, String> params = af.getMapParams();
String origin = params.get("Origin");
float pct = origin.equals("Battlefield") ? .8f : .667f;
Random r = MyRandom.random;
if (abCost != null) {
// AI currently disabled for these costs
if (abCost.getSacCost() && !abCost.getSacThis()) {
//only sacrifice something that's supposed to be sacrificed
String type = abCost.getSacType();
CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
typeList = typeList.getValidCards(type.split(","), source.getController(), source);
if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null)
return false;
}
if (abCost.getLifeCost()) {
if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4)
return false;
}
if (abCost.getDiscardCost()) return false;
if (abCost.getSubCounter()) {
// A card has a 25% chance per counter to be able to pass through here
// 4+ counters will always pass. 0 counters will never
int currentNum = source.getCounters(abCost.getCounterType());
double percent = .25 * (currentNum / abCost.getCounterNum());
if (percent <= r.nextFloat())
return false;
}
}
if (!ComputerUtil.canPayCost(sa))
return false;
// prevent run-away activations - first time will always return true
boolean chance = r.nextFloat() <= Math.pow(.6667, sa.getRestrictions().getNumberTurnActivations());
Target tgt = af.getAbTgt();
if (tgt != null) {
if (!changeKnownPreferredTarget(af, sa, false))
return false;
} else {
// non-targeted retrieval
CardList retrieval = null;
if (params.containsKey("Defined")) {
// add hooks into AF_Defined function
retrieval = knownDetermineDefined(sa, params.get("Defined"), origin);
}
if (retrieval == null || retrieval.isEmpty())
return false;
if (retrieval.get(0) == source) {
if (origin.equals("Graveyard")) {
// return this card from graveyard: cards like Hammer of Bogardan
// in general this is cool, but we should add some type of restrictions
} else if (origin.equals("Battlefield")) {
// return this card from battlefield: cards like Blinking Spirit
// in general this should only be used to protect from Imminent Harm (dying or losing control of)
return false;
}
}
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
chance &= subAb.chkAI_Drawback();
return ((r.nextFloat() < pct) && chance);
}
/**
* <p>changeKnownOriginPlayDrawbackAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean.
*/
private static boolean changeKnownOriginPlayDrawbackAI(AbilityFactory af, SpellAbility sa) {
if (sa.getTarget() == null)
return true;
return changeKnownPreferredTarget(af, sa, false);
}
/**
* <p>changeKnownPreferredTarget.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param mandatory a boolean.
* @return a boolean.
*/
private static boolean changeKnownPreferredTarget(AbilityFactory af, SpellAbility sa, boolean mandatory) {
HashMap<String, String> params = af.getMapParams();
Card source = sa.getSourceCard();
String origin = params.get("Origin");
String destination = params.get("Destination");
Target tgt = af.getAbTgt();
if (tgt != null)
tgt.resetTargets();
CardList list = AllZoneUtil.getCardsInZone(origin);
list = list.getValidCards(tgt.getValidTgts(), AllZone.getComputerPlayer(), source);
if (list.size() < tgt.getMinTargets(sa.getSourceCard(), sa))
return false;
// Narrow down the list:
if (origin.equals("Battlefield")) {
// filter out untargetables
list = list.getTargetableCards(source);
// if Destination is hand, either bounce opponents dangerous stuff or save my about to die stuff
// if Destination is exile, filter out my cards
} else if (origin.equals("Graveyard")) {
// Retrieve from Graveyard to:
}
// for now only bounce opponents stuff, but consider my stuff that might die
if (destination.equals("Exile") || origin.equals("Battlefield"))
list = list.getController(AllZone.getHumanPlayer());
if (list.isEmpty())
return false;
if (!mandatory && list.size() < tgt.getMinTargets(sa.getSourceCard(), sa))
return false;
// target loop
while (tgt.getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) {
// AI Targeting
Card choice = null;
if (!list.isEmpty()) {
Card mostExpensive = CardFactoryUtil.AI_getMostExpensivePermanent(list, af.getHostCard(), false);
if (destination.equals("Battlefield") || origin.equals("Battlefield")) {
if (mostExpensive.isCreature()) {
//if a creature is most expensive take the best one
if (destination.equals("Exile")) // If Exiling things, don't give bonus to Tokens
choice = CardFactoryUtil.AI_getBestCreature(list);
else
choice = CardFactoryUtil.AI_getBestCreatureToBounce(list);
} else
choice = mostExpensive;
} else {
// TODO: AI needs more improvement to it's retrieval (reuse some code from spReturn here)
list.shuffle();
choice = list.get(0);
}
}
if (choice == null) { // can't find anything left
if (tgt.getNumTargeted() == 0 || tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
if (!mandatory)
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
list.remove(choice);
tgt.addTarget(choice);
}
return true;
}
/**
* <p>changeKnownUnpreferredTarget.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param mandatory a boolean.
* @return a boolean.
*/
private static boolean changeKnownUnpreferredTarget(AbilityFactory af, SpellAbility sa, boolean mandatory) {
if (!mandatory)
return false;
HashMap<String, String> params = af.getMapParams();
Card source = sa.getSourceCard();
String origin = params.get("Origin");
String destination = params.get("Destination");
Target tgt = af.getAbTgt();
CardList list = AllZoneUtil.getCardsInZone(origin);
list = list.getValidCards(tgt.getValidTgts(), AllZone.getComputerPlayer(), source);
// Narrow down the list:
if (origin.equals("Battlefield")) {
// filter out untargetables
list = list.getTargetableCards(source);
// if Destination is hand, either bounce opponents dangerous stuff or save my about to die stuff
// if Destination is exile, filter out my cards
} else if (origin.equals("Graveyard")) {
// Retrieve from Graveyard to:
}
for (Card c : tgt.getTargetCards())
list.remove(c);
if (list.isEmpty())
return false;
// target loop
while (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
// AI Targeting
Card choice = null;
if (!list.isEmpty()) {
if (CardFactoryUtil.AI_getMostExpensivePermanent(list, af.getHostCard(), false).isCreature()
&& (destination.equals("Battlefield") || origin.equals("Battlefield")))
choice = CardFactoryUtil.AI_getBestCreatureToBounce(list); //if a creature is most expensive take the best
else if (destination.equals("Battlefield") || origin.equals("Battlefield"))
choice = CardFactoryUtil.AI_getMostExpensivePermanent(list, af.getHostCard(), false);
else {
// TODO: AI needs more improvement to it's retrieval (reuse some code from spReturn here)
list.shuffle();
choice = list.get(0);
}
}
if (choice == null) { // can't find anything left
if (tgt.getNumTargeted() == 0 || tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
list.remove(choice);
tgt.addTarget(choice);
}
return true;
}
/**
* <p>changeKnownOriginTriggerAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param mandatory a boolean.
* @return a boolean.
*/
private static boolean changeKnownOriginTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory) {
if (!ComputerUtil.canPayCost(sa))
return false;
if (sa.getTarget() == null) // Just in case of Defined cases
; // do nothing
else if (changeKnownPreferredTarget(af, sa, mandatory)) {
; // do nothing
} else if (!changeKnownUnpreferredTarget(af, sa, mandatory))
return false;
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
return subAb.doTrigger(mandatory);
return true;
}
/**
* <p>changeKnownOriginStackDescription.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a {@link java.lang.String} object.
*/
private static String changeKnownOriginStackDescription(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
StringBuilder sb = new StringBuilder();
Card host = af.getHostCard();
if (!(sa instanceof Ability_Sub))
sb.append(host.getName()).append(" -");
sb.append(" ");
String destination = params.get("Destination");
String origin = params.get("Origin");
StringBuilder sbTargets = new StringBuilder();
ArrayList<Card> tgts;
if (af.getAbTgt() != null)
tgts = af.getAbTgt().getTargetCards();
else {
// otherwise add self to list and go from there
tgts = new ArrayList<Card>();
for (Card c : knownDetermineDefined(sa, params.get("Defined"), origin)) {
tgts.add(c);
}
}
for (Card c : tgts)
sbTargets.append(" ").append(c.getName());
String targetname = sbTargets.toString();
String pronoun = tgts.size() > 1 ? " their " : " its ";
String fromGraveyard = " from the graveyard";
if (destination.equals("Battlefield")) {
sb.append("Put").append(targetname);
if (origin.equals("Graveyard"))
sb.append(fromGraveyard);
sb.append(" onto the battlefield");
if (params.containsKey("Tapped"))
sb.append(" tapped");
if (params.containsKey("GainControl"))
sb.append(" under your control");
sb.append(".");
}
if (destination.equals("Hand")) {
sb.append("Return").append(targetname);
if (origin.equals("Graveyard"))
sb.append(fromGraveyard);
sb.append(" to").append(pronoun).append("owners hand.");
}
if (destination.equals("Library")) {
if (params.containsKey("Shuffle")) { // for things like Gaea's Blessing
sb.append("Shuffle").append(targetname);
sb.append(" into").append(pronoun).append("owner's library.");
} else {
sb.append("Put").append(targetname);
if (origin.equals("Graveyard"))
sb.append(fromGraveyard);
// this needs to be zero indexed. Top = 0, Third = 2, -1 = Bottom
int libraryPosition = params.containsKey("LibraryPosition") ? Integer.parseInt(params.get("LibraryPosition")) : 0;
if (libraryPosition == -1)
sb.append(" on the bottom of").append(pronoun).append("owner's library.");
else if (libraryPosition == 0)
sb.append(" on top of").append(pronoun).append("owner's library.");
else
sb.append(" ").append(libraryPosition + 1).append(" from the top of").append(pronoun).append("owner's library.");
}
}
if (destination.equals("Exile")) {
sb.append("Exile").append(targetname);
if (origin.equals("Graveyard"))
sb.append(fromGraveyard);
sb.append(".");
}
if (destination.equals("Graveyard")) {
sb.append("Put").append(targetname);
sb.append(" from ").append(origin);
sb.append(" into").append(pronoun).append("owner's graveyard.");
}
Ability_Sub abSub = sa.getSubAbility();
if (abSub != null) {
sb.append(abSub.getStackDescription());
}
return sb.toString();
}
/**
* <p>changeKnownOriginResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void changeKnownOriginResolve(AbilityFactory af, SpellAbility sa) {
ArrayList<Card> tgtCards;
HashMap<String, String> params = af.getMapParams();
Target tgt = af.getAbTgt();
Player player = sa.getActivatingPlayer();
String destination = params.get("Destination");
String origin = params.get("Origin");
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = new ArrayList<Card>();
for (Card c : knownDetermineDefined(sa, params.get("Defined"), origin)) {
tgtCards.add(c);
}
}
if (tgtCards.size() != 0) {
for (Card tgtC : tgtCards) {
PlayerZone originZone = AllZone.getZone(tgtC);
// if Target isn't in the expected Zone, continue
if (!originZone.is(origin))
continue;
if (tgt != null && origin.equals("Battlefield")) {
// check targeting
if (!CardFactoryUtil.canTarget(sa.getSourceCard(), tgtC))
continue;
}
Player pl = player;
if (!destination.equals("Battlefield"))
pl = tgtC.getOwner();
if (destination.equals("Library")) {
// library position is zero indexed
int libraryPosition = params.containsKey("LibraryPosition") ? Integer.parseInt(params.get("LibraryPosition")) : 0;
AllZone.getGameAction().moveToLibrary(tgtC, libraryPosition);
if (params.containsKey("Shuffle")) // for things like Gaea's Blessing
tgtC.getOwner().shuffle();
} else {
if (destination.equals("Battlefield")) {
if (params.containsKey("Tapped") || params.containsKey("Ninjutsu"))
tgtC.tap();
if (params.containsKey("GainControl"))
tgtC.setController(sa.getActivatingPlayer());
AllZone.getGameAction().moveTo(AllZone.getZone(destination, tgtC.getController()), tgtC);
if (params.containsKey("Ninjutsu") || params.containsKey("Attacking")) {
AllZone.getCombat().addAttacker(tgtC);
AllZone.getCombat().addUnblockedAttacker(tgtC);
}
} else {
AllZone.getGameAction().moveTo(AllZone.getZone(destination, pl), tgtC);
}
}
}
}
}
// **************************** Known Utility **************************************
/**
* <p>knownDetermineDefined.</p>
*
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param defined a {@link java.lang.String} object.
* @param origin a {@link java.lang.String} object.
* @return a {@link forge.CardList} object.
*/
private static CardList knownDetermineDefined(SpellAbility sa, String defined, String origin) {
// TODO: this function should return a ArrayList<Card> and then be handled by the callees
CardList grave = AllZoneUtil.getCardsInZone(origin, sa.getActivatingPlayer());
CardList ret = new CardList();
if (defined != null && defined.equals("Top")) {
// the "top" of the graveyard, is the last to be added to the graveyard list?
if (grave.size() == 0)
return null;
ret.add(grave.get(grave.size() - 1));
return ret;
}
ret.addAll(AbilityFactory.getDefinedCards(sa.getSourceCard(), defined, sa).toArray());
return ret;
}
// *************************************************************************************
// ************************** ChangeZoneAll ********************************************
// ************ All is non-targeted and should occur similarly to Hidden ***************
// ******* Instead of choosing X of type on resolution, all on type go *****************
// *************************************************************************************
/**
* <p>createAbilityChangeZoneAll.</p>
*
* @param AF a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityChangeZoneAll(final AbilityFactory AF) {
final SpellAbility abChangeZone = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) {
private static final long serialVersionUID = 3728332812890211671L;
public boolean canPlayAI() {
return changeZoneAllCanPlayAI(AF, this);
}
@Override
public void resolve() {
changeZoneAllResolve(AF, this);
}
@Override
public String getStackDescription() {
return changeZoneAllDescription(AF, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return changeZoneAllCanPlayAI(AF, this);
}
};
setMiscellaneous(AF, abChangeZone);
return abChangeZone;
}
/**
* <p>createSpellChangeZoneAll.</p>
*
* @param AF a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellChangeZoneAll(final AbilityFactory AF) {
final SpellAbility spChangeZone = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) {
private static final long serialVersionUID = 3270484211099902059L;
public boolean canPlayAI() {
return changeZoneAllCanPlayAI(AF, this);
}
@Override
public void resolve() {
changeZoneAllResolve(AF, this);
}
@Override
public String getStackDescription() {
return changeZoneAllDescription(AF, this);
}
};
setMiscellaneous(AF, spChangeZone);
return spChangeZone;
}
/**
* <p>createDrawbackChangeZoneAll.</p>
*
* @param AF a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createDrawbackChangeZoneAll(final AbilityFactory AF) {
final SpellAbility dbChangeZone = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) {
private static final long serialVersionUID = 3270484211099902059L;
@Override
public void resolve() {
changeZoneAllResolve(AF, this);
}
@Override
public boolean chkAI_Drawback() {
return changeZoneAllPlayDrawbackAI(AF, this);
}
@Override
public String getStackDescription() {
return changeZoneAllDescription(AF, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return changeZoneAllCanPlayAI(AF, this);
}
};
setMiscellaneous(AF, dbChangeZone);
return dbChangeZone;
}
/**
* <p>changeZoneAllCanPlayAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean.
*/
private static boolean changeZoneAllCanPlayAI(AbilityFactory af, SpellAbility sa) {
// Change Zone All, can be any type moving from one zone to another
Cost abCost = af.getAbCost();
Card source = af.getHostCard();
HashMap<String, String> params = af.getMapParams();
String destination = params.get("Destination");
String origin = params.get("Origin");
if (abCost != null) {
// AI currently disabled for these costs
if (abCost.getSacCost()) {
// Sac is ok in general, but should add some decision making based off what we Sacrifice and what we might get
}
if (abCost.getLifeCost()) {
if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4)
return false;
}
if (abCost.getDiscardCost()) return false;
if (abCost.getSubCounter())
; // subcounter is fine
}
if (!ComputerUtil.canPayCost(sa))
return false;
Random r = MyRandom.random;
// prevent run-away activations - first time will always return true
boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());
// TODO: targeting with ChangeZoneAll
// really two types of targeting.
// Target Player has all their types change zones
// or target permanent and do something relative to that permanent
// ex. "Return all Auras attached to target"
// ex. "Return all blocking/blocked by target creature"
CardList humanType = AllZoneUtil.getCardsInZone(origin, AllZone.getHumanPlayer());
humanType = AbilityFactory.filterListByType(humanType, params.get("ChangeType"), sa);
CardList computerType = AllZoneUtil.getCardsInZone(origin, AllZone.getComputerPlayer());
computerType = AbilityFactory.filterListByType(computerType, params.get("ChangeType"), sa);
// TODO: improve restrictions on when the AI would want to use this
// spBounceAll has some AI we can compare to.
if (origin.equals("Hand")) {
} else if (origin.equals("Library")) {
} else if (origin.equals("Battlefield")) {
// this statement is assuming the AI is trying to use this spell offensively
// if the AI is using it defensively, then something else needs to occur
// if only creatures are affected evaluate both lists and pass only if human creatures are more valuable
if (humanType.getNotType("Creature").size() == 0 && computerType.getNotType("Creature").size() == 0) {
if (CardFactoryUtil.evaluateCreatureList(computerType) + 200 >= CardFactoryUtil.evaluateCreatureList(humanType))
return false;
} // otherwise evaluate both lists by CMC and pass only if human permanents are more valuable
else if (CardFactoryUtil.evaluatePermanentList(computerType) + 3 >= CardFactoryUtil.evaluatePermanentList(humanType))
return false;
// Don't cast during main1?
if (AllZone.getPhase().is(Constant.Phase.Main1, AllZone.getComputerPlayer()))
return false;
} else if (origin.equals("Graveyard")) {
Target tgt = af.getAbTgt();
if (tgt != null) {
if (AllZoneUtil.getPlayerGraveyard(AllZone.getHumanPlayer()).isEmpty())
return false;
tgt.resetTargets();
tgt.addTarget(AllZone.getHumanPlayer());
}
} else if (origin.equals("Exile")) {
} else if (origin.equals("Stack")) {
// time stop can do something like this:
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip
// DBSKipToPhase | DB$SkipToPhase | Phase$ Cleanup
// otherwise, this situation doesn't exist
return false;
} else if (origin.equals("Sideboard")) {
// This situation doesn't exist
return false;
}
if (destination.equals(Constant.Zone.Battlefield)) {
if (params.get("GainControl") != null) {
// Check if the cards are valuable enough
if (humanType.getNotType("Creature").size() == 0 && computerType.getNotType("Creature").size() == 0) {
if (CardFactoryUtil.evaluateCreatureList(computerType) + CardFactoryUtil.evaluateCreatureList(humanType) < 400)
return false;
} // otherwise evaluate both lists by CMC and pass only if human permanents are less valuable
else if (CardFactoryUtil.evaluatePermanentList(computerType) + CardFactoryUtil.evaluatePermanentList(humanType) < 6)
return false;
} else {
// don't activate if human gets more back than AI does
if (humanType.getNotType("Creature").size() == 0 && computerType.getNotType("Creature").size() == 0) {
if (CardFactoryUtil.evaluateCreatureList(computerType) <= CardFactoryUtil.evaluateCreatureList(humanType) + 100)
return false;
} // otherwise evaluate both lists by CMC and pass only if human permanents are less valuable
else if (CardFactoryUtil.evaluatePermanentList(computerType) <= CardFactoryUtil.evaluatePermanentList(humanType) + 2)
return false;
}
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
chance &= subAb.chkAI_Drawback();
return ((r.nextFloat() < .8 || sa.isTrigger()) && chance);
}
/**
* <p>changeZoneAllPlayDrawbackAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean.
*/
private static boolean changeZoneAllPlayDrawbackAI(AbilityFactory af, SpellAbility sa) {
// if putting cards from hand to library and parent is drawing cards
// make sure this will actually do something:
return true;
}
/**
* <p>changeZoneAllDescription.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a {@link java.lang.String} object.
*/
private static String changeZoneAllDescription(AbilityFactory af, SpellAbility sa) {
// TODO: build Stack Description will need expansion as more cards are added
StringBuilder sb = new StringBuilder();
Card host = af.getHostCard();
if (!(sa instanceof Ability_Sub))
sb.append(host.getName()).append(" -");
sb.append(" ");
String[] desc = sa.getDescription().split(":");
if (desc.length > 1)
sb.append(desc[1]);
else
sb.append(desc[0]);
Ability_Sub abSub = sa.getSubAbility();
if (abSub != null) {
sb.append(abSub.getStackDescription());
}
return sb.toString();
}
/**
* <p>changeZoneAllResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void changeZoneAllResolve(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
String destination = params.get("Destination");
String origin = params.get("Origin");
CardList cards = null;
ArrayList<Player> tgtPlayers = null;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtPlayers = tgt.getTargetPlayers();
else if (params.containsKey("Defined")) // Make sure Defined exists to use it
tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa);
if (tgtPlayers == null || tgtPlayers.isEmpty())
cards = AllZoneUtil.getCardsInZone(origin);
else
cards = AllZoneUtil.getCardsInZone(origin, tgtPlayers.get(0));
cards = AbilityFactory.filterListByType(cards, params.get("ChangeType"), sa);
if (params.containsKey("ForgetOtherRemembered"))
sa.getSourceCard().clearRemembered();
String remember = params.get("RememberChanged");
// I don't know if library position is necessary. It's here if it is, just in case
int libraryPos = params.containsKey("LibraryPosition") ? Integer.parseInt(params.get("LibraryPosition")) : 0;
for (Card c : cards) {
if (destination.equals("Battlefield") && params.containsKey("Tapped"))
c.tap();
if (params.containsKey("GainControl")) {
c.setController(sa.getActivatingPlayer());
AllZone.getGameAction().moveToPlay(c, sa.getActivatingPlayer());
} else
AllZone.getGameAction().moveTo(destination, c, libraryPos);
if (remember != null)
sa.getSourceCard().addRemembered(c);
}
// if Shuffle parameter exists, and any amount of cards were owned by that player, then shuffle that library
if (params.containsKey("Shuffle")) {
if (cards.getOwner(AllZone.getHumanPlayer()).size() > 0)
AllZone.getHumanPlayer().shuffle();
if (cards.getOwner(AllZone.getComputerPlayer()).size() > 0)
AllZone.getComputerPlayer().shuffle();
}
}
}