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.Iterator; import java.util.Random; /** * <p>AbilityFactory_ZoneAffecting class.</p> * * @author Forge * @version $Id: $ */ public class AbilityFactory_ZoneAffecting { //********************************************************************** //******************************* DRAW ********************************* //********************************************************************** /** * <p>createAbilityDraw.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createAbilityDraw(final AbilityFactory af) { final SpellAbility abDraw = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = 5445572699000471299L; @Override public String getStackDescription() { return drawStackDescription(af, this); } @Override public boolean canPlayAI() { return drawCanPlayAI(af, this); } @Override public void resolve() { drawResolve(af, this); } @Override public boolean doTrigger(boolean mandatory) { return drawTrigger(af, this, mandatory); } }; return abDraw; } /** * <p>createSpellDraw.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createSpellDraw(final AbilityFactory af) { final SpellAbility spDraw = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = -4990932993654533449L; @Override public String getStackDescription() { return drawStackDescription(af, this); } @Override public boolean canPlayAI() { return drawCanPlayAI(af, this); } @Override public void resolve() { drawResolve(af, this); } }; return spDraw; } /** * <p>createDrawbackDraw.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createDrawbackDraw(final AbilityFactory af) { final SpellAbility dbDraw = new Ability_Sub(af.getHostCard(), af.getAbTgt()) { private static final long serialVersionUID = -4990932993654533449L; @Override public String getStackDescription() { return drawStackDescription(af, this); } @Override public void resolve() { drawResolve(af, this); } @Override public boolean chkAI_Drawback() { return drawTargetAI(af, this, false, false); } @Override public boolean doTrigger(boolean mandatory) { return drawTrigger(af, this, mandatory); } }; return dbDraw; } /** * <p>drawStackDescription.</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 drawStackDescription(AbilityFactory af, SpellAbility sa) { HashMap<String, String> params = af.getMapParams(); StringBuilder sb = new StringBuilder(); if (!(sa instanceof Ability_Sub)) sb.append(sa.getSourceCard().getName()).append(" - "); else sb.append(" "); String conditionDesc = params.get("ConditionDescription"); if (conditionDesc != null) sb.append(conditionDesc).append(" "); ArrayList<Player> tgtPlayers; Target tgt = af.getAbTgt(); if (tgt != null) tgtPlayers = tgt.getTargetPlayers(); else tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); if (tgtPlayers.size() > 0) { Iterator<Player> it = tgtPlayers.iterator(); while(it.hasNext()) { sb.append(it.next().toString()); if(it.hasNext()) sb.append(" and "); } int numCards = 1; if (params.containsKey("NumCards")) numCards = AbilityFactory.calculateAmount(sa.getSourceCard(), params.get("NumCards"), sa); if(tgtPlayers.size() > 1) sb.append(" each"); sb.append(" draw"); if(tgtPlayers.size() == 1) sb.append("s"); sb.append(" (").append(numCards).append(")"); if (params.containsKey("NextUpkeep")) sb.append(" at the beginning of the next upkeep"); sb.append("."); } Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { sb.append(abSub.getStackDescription()); } return sb.toString(); } /** * <p>drawCanPlayAI.</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 drawCanPlayAI(final AbilityFactory af, SpellAbility sa) { HashMap<String, String> params = af.getMapParams(); // AI cannot use this properly until he can use SAs during Humans turn if (!ComputerUtil.canPayCost(sa)) return false; Target tgt = af.getAbTgt(); Card source = sa.getSourceCard(); Cost abCost = af.getAbCost(); if (abCost != null) { // AI currently disabled for these costs if (abCost.getSacCost()) { return false; } if (abCost.getLifeCost()) { if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) return false; } if (abCost.getDiscardCost()) return false; if (abCost.getSubCounter()) { if (abCost.getCounterType().equals(Counters.P1P1)) return false; // Other counters should be used } } boolean bFlag = drawTargetAI(af, sa, true, false); if (!bFlag) return false; if (tgt != null) { ArrayList<Player> players = tgt.getTargetPlayers(); if (players.size() > 0 && players.get(0).isHuman()) return true; } //Don't use draw abilities before main 2 if possible if (AllZone.getPhase().isBefore(Constant.Phase.Main2) && !params.containsKey("ActivatingPhases")) return false; //Don't tap creatures that may be able to block if (AbilityFactory.waitForBlocking(sa)) return false; double chance = .4; // 40 percent chance of drawing with instant speed stuff if (AbilityFactory.isSorcerySpeed(sa)) chance = .667; // 66.7% chance for sorcery speed if ((AllZone.getPhase().is(Constant.Phase.End_Of_Turn) && AllZone.getPhase().isNextTurn(AllZone.getComputerPlayer()))) chance = .9; // 90% for end of opponents turn Random r = MyRandom.random; boolean randomReturn = r.nextFloat() <= Math.pow(chance, source.getAbilityUsed() + 1); if (AbilityFactory.playReusable(sa)) randomReturn = true; Ability_Sub subAb = sa.getSubAbility(); if (subAb != null) randomReturn &= subAb.chkAI_Drawback(); return randomReturn; } /** * <p>drawTargetAI.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param primarySA a boolean. * @param mandatory a boolean. * @return a boolean. */ private static boolean drawTargetAI(AbilityFactory af, SpellAbility sa, boolean primarySA, boolean mandatory) { Target tgt = af.getAbTgt(); HashMap<String, String> params = af.getMapParams(); Card source = sa.getSourceCard(); int computerHandSize = AllZoneUtil.getCardsInZone(Constant.Zone.Hand, AllZone.getComputerPlayer()).size(); int humanLibrarySize = AllZoneUtil.getCardsInZone(Constant.Zone.Library, AllZone.getHumanPlayer()).size(); int computerLibrarySize = AllZoneUtil.getCardsInZone(Constant.Zone.Library, AllZone.getComputerPlayer()).size(); int computerMaxHandSize = AllZone.getComputerPlayer().getMaxHandSize(); int numCards = 1; if (params.containsKey("NumCards")) numCards = AbilityFactory.calculateAmount(sa.getSourceCard(), params.get("NumCards"), sa); boolean xPaid = false; String num = params.get("NumCards"); if (num != null && num.equals("X") && source.getSVar(num).equals("Count$xPaid")) { // Set PayX here to maximum value. if (sa instanceof Ability_Sub) numCards = Integer.parseInt(source.getSVar("PayX")); else { numCards = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(numCards)); } xPaid = true; } // TODO: if xPaid and one of the below reasons would fail, instead of bailing // reduce toPay amount to acceptable level if (tgt != null) { // ability is targeted tgt.resetTargets(); boolean canTgtHuman = AllZone.getHumanPlayer().canTarget(source); boolean canTgtComp = AllZone.getComputerPlayer().canTarget(source); boolean tgtHuman = false; if (!canTgtHuman && !canTgtComp) return false; if (canTgtHuman && !AllZone.getHumanPlayer().cantLose() && numCards >= humanLibrarySize) { // Deck the Human? DO IT! tgt.addTarget(AllZone.getHumanPlayer()); return true; } if (numCards >= computerLibrarySize) { if (xPaid) { numCards = computerLibrarySize - 1; source.setSVar("PayX", Integer.toString(numCards)); } else { // Don't deck your self if (!mandatory) return false; tgtHuman = true; } } if (computerHandSize + numCards > computerMaxHandSize && AllZone.getPhase().getPlayerTurn().isComputer()) { if (xPaid) { numCards = computerMaxHandSize - computerHandSize; source.setSVar("PayX", Integer.toString(numCards)); } else { // Don't draw too many cards and then risk discarding cards at EOT if (!(params.containsKey("NextUpkeep") || sa instanceof Ability_Sub) && !mandatory) return false; } } if (numCards == 0) return false; if ((!tgtHuman || !canTgtHuman) && canTgtComp) tgt.addTarget(AllZone.getComputerPlayer()); else tgt.addTarget(AllZone.getHumanPlayer()); } else { // TODO: consider if human is the defined player // ability is not targeted if (numCards >= computerLibrarySize) { // Don't deck yourself if (!mandatory) return false; } if (computerHandSize + numCards > computerMaxHandSize && AllZone.getPhase().getPlayerTurn().isComputer()) { // Don't draw too many cards and then risk discarding cards at EOT if (!(params.containsKey("NextUpkeep") || sa instanceof Ability_Sub) && !mandatory) return false; } } return true; }// drawTargetAI() /** * <p>drawTrigger.</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 drawTrigger(AbilityFactory af, SpellAbility sa, boolean mandatory) { if (!ComputerUtil.canPayCost(sa)) // If there is a cost payment return false; if (!drawTargetAI(af, sa, false, mandatory)) return false; // check SubAbilities DoTrigger? Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { return abSub.doTrigger(mandatory); } return true; } /** * <p>drawResolve.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. */ private static void drawResolve(final AbilityFactory af, final SpellAbility sa) { HashMap<String, String> params = af.getMapParams(); Card source = sa.getSourceCard(); int numCards = 1; if (params.containsKey("NumCards")) numCards = AbilityFactory.calculateAmount(sa.getSourceCard(), params.get("NumCards"), sa); ArrayList<Player> tgtPlayers; Target tgt = af.getAbTgt(); if (tgt != null) tgtPlayers = tgt.getTargetPlayers(); else tgtPlayers = AbilityFactory.getDefinedPlayers(source, params.get("Defined"), sa); boolean optional = params.containsKey("OptionalDecider"); boolean slowDraw = params.containsKey("NextUpkeep"); for (Player p : tgtPlayers) { if (tgt == null || p.canTarget(af.getHostCard())) { if (optional) { if (p.isComputer()) { if (numCards >= AllZoneUtil.getPlayerCardsInLibrary(p).size()) { // AI shouldn't itself continue; } } else { StringBuilder sb = new StringBuilder(); sb.append("Do you want to draw ").append(numCards).append(" cards(s)"); if (slowDraw) sb.append(" next upkeep"); sb.append("?"); if (!GameActionUtil.showYesNoDialog(sa.getSourceCard(), sb.toString())) continue; } } if (slowDraw) for (int i = 0; i < numCards; i++) p.addSlowtripList(source); else p.drawCards(numCards); } } }//drawResolve() //********************************************************************** //******************************* MILL ********************************* //********************************************************************** /** * <p>createAbilityMill.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createAbilityMill(final AbilityFactory af) { final SpellAbility abMill = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = 5445572699000471299L; @Override public String getStackDescription() { return millStackDescription(this, af); } @Override public boolean canPlayAI() { return millCanPlayAI(af, this); } @Override public void resolve() { millResolve(af, this); } @Override public boolean doTrigger(boolean mandatory) { return millCanPlayAI(af, this); } }; return abMill; } /** * <p>createSpellMill.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createSpellMill(final AbilityFactory af) { final SpellAbility spMill = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = -4990932993654533449L; @Override public String getStackDescription() { return millStackDescription(this, af); } @Override public boolean canPlayAI() { return millCanPlayAI(af, this); } @Override public void resolve() { millResolve(af, this); } }; return spMill; } /** * <p>createDrawbackMill.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createDrawbackMill(final AbilityFactory af) { final SpellAbility dbMill = new Ability_Sub(af.getHostCard(), af.getAbTgt()) { private static final long serialVersionUID = -4990932993654533449L; @Override public String getStackDescription() { return millStackDescription(this, af); } @Override public void resolve() { millResolve(af, this); } @Override public boolean chkAI_Drawback() { return millDrawback(af, this); } @Override public boolean doTrigger(boolean mandatory) { return millTargetAI(af, this, false); } }; return dbMill; } /** * <p>millStackDescription.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link java.lang.String} object. */ private static String millStackDescription(SpellAbility sa, AbilityFactory af) { HashMap<String, String> params = af.getMapParams(); StringBuilder sb = new StringBuilder(); int numCards = AbilityFactory.calculateAmount(sa.getSourceCard(), params.get("NumCards"), sa); ArrayList<Player> tgtPlayers; Target tgt = af.getAbTgt(); if (tgt != null) tgtPlayers = tgt.getTargetPlayers(); else tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); if (!(sa instanceof Ability_Sub)) sb.append(sa.getSourceCard().getName()).append(" - "); else sb.append(" "); String conditionDesc = params.get("ConditionDescription"); if (conditionDesc != null) sb.append(conditionDesc).append(" "); for (Player p : tgtPlayers) sb.append(p.toString()).append(" "); String destination = params.get("Destination"); if (destination == null || destination.equals(Constant.Zone.Graveyard)) sb.append("mills "); else if (destination.equals(Constant.Zone.Exile)) sb.append("exiles "); sb.append(numCards); sb.append(" card"); if (numCards != 1) sb.append("s"); sb.append(" from his or her library."); Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { sb.append(abSub.getStackDescription()); } return sb.toString(); } /** * <p>millCanPlayAI.</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 millCanPlayAI(final AbilityFactory af, SpellAbility sa) { HashMap<String, String> params = af.getMapParams(); if (!ComputerUtil.canPayCost(sa)) return false; Card source = sa.getSourceCard(); Cost abCost = af.getAbCost(); if (abCost != null) { // AI currently disabled for these costs if (abCost.getSacCost()) { return false; } if (abCost.getLifeCost()) { if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) return false; } if (abCost.getDiscardCost()) return false; if (abCost.getSubCounter()) { if (abCost.getCounterType().equals(Counters.P1P1)) return false; // Other counters should be used } } if (!millTargetAI(af, sa, false)) return false; Random r = MyRandom.random; //Don't use draw abilities before main 2 if possible if (AllZone.getPhase().isBefore(Constant.Phase.Main2) && !params.containsKey("ActivatingPhases")) return false; //Don't tap creatures that may be able to block if (AbilityFactory.waitForBlocking(sa)) return false; double chance = .4; // 40 percent chance of milling with instant speed stuff if (AbilityFactory.isSorcerySpeed(sa)) chance = .667; // 66.7% chance for sorcery speed if ((AllZone.getPhase().is(Constant.Phase.End_Of_Turn) && AllZone.getPhase().isNextTurn(AllZone.getComputerPlayer()))) chance = .9; // 90% for end of opponents turn boolean randomReturn = r.nextFloat() <= Math.pow(chance, source.getAbilityUsed() + 1); if (AbilityFactory.playReusable(sa)) randomReturn = true; // some other variables here, like deck size, and phase and other fun stuff if (params.get("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")) { // Set PayX here to maximum value. int cardsToDiscard = Math.min(ComputerUtil.determineLeftoverMana(sa), AllZoneUtil.getCardsInZone(Constant.Zone.Library, AllZone.getHumanPlayer()).size()); source.setSVar("PayX", Integer.toString(cardsToDiscard)); } return randomReturn; } /** * <p>millTargetAI.</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 millTargetAI(AbilityFactory af, SpellAbility sa, boolean mandatory) { Target tgt = af.getAbTgt(); HashMap<String, String> params = af.getMapParams(); if (tgt != null) { tgt.resetTargets(); Card source = sa.getSourceCard(); if (!AllZone.getHumanPlayer().canTarget(source)) { if (mandatory && AllZone.getComputerPlayer().canTarget(source)) { tgt.addTarget(AllZone.getComputerPlayer()); return true; } return false; } int numCards = AbilityFactory.calculateAmount(sa.getSourceCard(), params.get("NumCards"), sa); CardList pLibrary = AllZoneUtil.getCardsInZone(Constant.Zone.Library, AllZone.getHumanPlayer()); if (pLibrary.size() == 0) { // deck already empty, no need to mill if (!mandatory) return false; tgt.addTarget(AllZone.getHumanPlayer()); return true; } if (numCards >= pLibrary.size()) { // Can Mill out Human's deck? Do it! tgt.addTarget(AllZone.getHumanPlayer()); return true; } // Obscure case when you know what your top card is so you might? want to mill yourself here // if (AI wants to mill self) // tgt.addTarget(AllZone.getComputerPlayer()); // else tgt.addTarget(AllZone.getHumanPlayer()); } return true; } /** * <p>millDrawback.</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 millDrawback(AbilityFactory af, SpellAbility sa) { if (!millTargetAI(af, sa, true)) return false; // check SubAbilities DoTrigger? Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { return abSub.chkAI_Drawback(); } return true; } /* TODO - not currently used anywhere. was it meant to be? private static boolean millTrigger(AbilityFactory af, SpellAbility sa, boolean mandatory) { if (!ComputerUtil.canPayCost(sa)) // If there is a cost payment return false; if (!millTargetAI(af, sa, mandatory)) return false; HashMap<String,String> params = af.getMapParams(); Card source = sa.getSourceCard(); if (params.get("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")){ // Set PayX here to maximum value. int cardsToDiscard = Math.min(ComputerUtil.determineLeftoverMana(sa), AllZoneUtil.getCardsInZone(Constant.Zone.Library, AllZone.getHumanPlayer()).size()); source.setSVar("PayX", Integer.toString(cardsToDiscard)); } // check SubAbilities DoTrigger? Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { return abSub.doTrigger(mandatory); } return true; } */ /** * <p>millResolve.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. */ private static void millResolve(final AbilityFactory af, final SpellAbility sa) { HashMap<String, String> params = af.getMapParams(); int numCards = AbilityFactory.calculateAmount(sa.getSourceCard(), params.get("NumCards"), sa); ArrayList<Player> tgtPlayers; Target tgt = af.getAbTgt(); if (tgt != null) tgtPlayers = tgt.getTargetPlayers(); else tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); String destination = params.get("Destination"); if (destination == null) destination = Constant.Zone.Graveyard; for (Player p : tgtPlayers) if (tgt == null || p.canTarget(af.getHostCard())) p.mill(numCards, destination); } ////////////////////// // //Discard stuff // ////////////////////// //NumCards - the number of cards to be discarded (may be integer or X) //Mode - the mode of discard - should match spDiscard // -Random // -TgtChoose // -RevealYouChoose // -RevealOppChoose // -RevealDiscardAll (defaults to Card if DiscardValid is missing) // -Hand //DiscardValid - a ValidCards syntax for acceptable cards to discard //UnlessType - a ValidCards expression for "discard x unless you discard a ..." //Examples: //A:SP$Discard | Cost$B | Tgt$TgtP | NumCards$2 | Mode$Random | SpellDescription$<...> //A:AB$Discard | Cost$U | ValidTgts$ Opponent | Mode$RevealYouChoose | NumCards$X | SpellDescription$<...> /** * <p>createAbilityDiscard.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createAbilityDiscard(final AbilityFactory af) { final SpellAbility abDiscard = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = 4348585353456736817L; @Override public String getStackDescription() { return discardStackDescription(af, this); } @Override public boolean canPlayAI() { return discardCanPlayAI(af, this); } @Override public void resolve() { discardResolve(af, this); } @Override public boolean doTrigger(boolean mandatory) { return discardTrigger(af, this, mandatory); } }; return abDiscard; } /** * <p>createSpellDiscard.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createSpellDiscard(final AbilityFactory af) { final SpellAbility spDiscard = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = 4348585353456736817L; @Override public String getStackDescription() { return discardStackDescription(af, this); } @Override public boolean canPlayAI() { return discardCanPlayAI(af, this); } @Override public void resolve() { discardResolve(af, this); } }; return spDiscard; } /** * <p>createDrawbackDiscard.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createDrawbackDiscard(final AbilityFactory af) { final SpellAbility dbDiscard = new Ability_Sub(af.getHostCard(), af.getAbTgt()) { private static final long serialVersionUID = 4348585353456736817L; @Override public String getStackDescription() { return discardStackDescription(af, this); } @Override public void resolve() { discardResolve(af, this); } @Override public boolean chkAI_Drawback() { return discardCheckDrawbackAI(af, this); } @Override public boolean doTrigger(boolean mandatory) { return discardTrigger(af, this, mandatory); } }; return dbDiscard; } /** * <p>discardResolve.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. */ private static void discardResolve(final AbilityFactory af, final SpellAbility sa) { Card source = sa.getSourceCard(); HashMap<String, String> params = af.getMapParams(); String mode = params.get("Mode"); ArrayList<Player> tgtPlayers; Target tgt = af.getAbTgt(); if (tgt != null) tgtPlayers = tgt.getTargetPlayers(); else tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); for (Player p : tgtPlayers) { if (tgt == null || p.canTarget(af.getHostCard())) { if (mode.equals("Hand")) { p.discardHand(sa); continue; } int numCards = 1; if (params.containsKey("NumCards")) numCards = AbilityFactory.calculateAmount(sa.getSourceCard(), params.get("NumCards"), sa); if (mode.equals("Random")) { p.discardRandom(numCards, sa); } else if (mode.equals("TgtChoose")) { if (params.containsKey("UnlessType")) { p.discardUnless(numCards, params.get("UnlessType"), sa); } else p.discard(numCards, sa, true); } else if (mode.equals("RevealDiscardAll")) { // Reveal CardList dPHand = AllZoneUtil.getPlayerHand(p); if (p.isHuman()) { // "reveal to computer" for information gathering } else { GuiUtils.getChoiceOptional("Revealed computer hand", dPHand.toArray()); } String valid = params.get("DiscardValid"); if (valid == null) valid = "Card"; if (valid.contains("X")) valid = valid.replace("X", Integer.toString(AbilityFactory.calculateAmount(source, "X", sa))); CardList dPChHand = dPHand.getValidCards(valid.split(","), source.getController(), source); // Reveal cards that will be discarded? for (Card c : dPChHand) { p.discard(c, sa); } } else if (mode.equals("RevealYouChoose") || mode.equals("RevealOppChoose")) { // Is Reveal you choose right? I think the wrong player is being used? CardList dPHand = AllZoneUtil.getPlayerHand(p); if (dPHand.size() != 0) { CardList dPChHand = new CardList(dPHand.toArray()); if (params.containsKey("DiscardValid")) { // Restrict card choices String[] dValid = params.get("DiscardValid").split(","); dPChHand = dPHand.getValidCards(dValid, source.getController(), source); } Player chooser = null; if (mode.equals("RevealYouChoose")) chooser = source.getController(); else chooser = source.getController().getOpponent(); if (chooser.isComputer()) { //AI for (int i = 0; i < numCards; i++) { if (dPChHand.size() > 0) { CardList dChoices = new CardList(); if (params.containsKey("DiscardValid")) { String dValid = params.get("DiscardValid"); if (dValid.contains("Creature") && !dValid.contains("nonCreature")) { Card c = CardFactoryUtil.AI_getBestCreature(dPChHand); if (c != null) dChoices.add(CardFactoryUtil.AI_getBestCreature(dPChHand)); } } CardListUtil.sortByTextLen(dPChHand); dChoices.add(dPChHand.get(0)); CardListUtil.sortCMC(dPChHand); dChoices.add(dPChHand.get(0)); Card dC = dChoices.get(CardUtil.getRandomIndex(dChoices)); dPChHand.remove(dC); CardList dCs = new CardList(); dCs.add(dC); GuiUtils.getChoiceOptional("Computer has chosen", dCs.toArray()); AllZone.getComputerPlayer().discard(dC, sa); // is this right? } } } else { //human GuiUtils.getChoiceOptional("Revealed computer hand", dPHand.toArray()); for (int i = 0; i < numCards; i++) { if (dPChHand.size() > 0) { Card dC = GuiUtils.getChoice("Choose a card to be discarded", dPChHand.toArray()); dPChHand.remove(dC); AllZone.getHumanPlayer().discard(dC, sa); // is this right? } } } } } } } }//discardResolve() /** * <p>discardStackDescription.</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 discardStackDescription(AbilityFactory af, SpellAbility sa) { HashMap<String, String> params = af.getMapParams(); String mode = params.get("Mode"); StringBuilder sb = new StringBuilder(); ArrayList<Player> tgtPlayers; Target tgt = af.getAbTgt(); if (tgt != null) tgtPlayers = tgt.getTargetPlayers(); else tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); if (!(sa instanceof Ability_Sub)) sb.append(sa.getSourceCard().getName()).append(" - "); else sb.append(" "); String conditionDesc = params.get("ConditionDescription"); if (conditionDesc != null) sb.append(conditionDesc).append(" "); if (tgtPlayers.size() > 0) { for (Player p : tgtPlayers) sb.append(p.toString()).append(" "); if (mode.equals("RevealYouChoose")) sb.append("reveals his or her hand.").append(" You choose ("); else if (mode.equals("RevealDiscardAll")) sb.append("reveals his or her hand. Discard ("); else sb.append("discards ("); int numCards = 1; if (params.containsKey("NumCards")) numCards = AbilityFactory.calculateAmount(sa.getSourceCard(), params.get("NumCards"), sa); if (mode.equals("Hand")) sb.append("his or her hand"); else if (mode.equals("RevealDiscardAll")) sb.append("All"); else sb.append(numCards); sb.append(")"); if (mode.equals("RevealYouChoose")) sb.append(" to discard"); else if (mode.equals("RevealDiscardAll")) { String valid = params.get("DiscardValid"); if (valid == null) valid = "Card"; sb.append(" of type: ").append(valid); } if (mode.equals("Random")) sb.append(" at random."); else sb.append("."); } Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) sb.append(abSub.getStackDescription()); return sb.toString(); }//discardStackDescription() /** * <p>discardCanPlayAI.</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 discardCanPlayAI(final AbilityFactory af, SpellAbility sa) { HashMap<String, String> params = af.getMapParams(); // AI cannot use this properly until he can use SAs during Humans turn if (!ComputerUtil.canPayCost(sa)) return false; Target tgt = af.getAbTgt(); Card source = sa.getSourceCard(); Cost abCost = af.getAbCost(); if (abCost != null) { // AI currently disabled for these costs if (abCost.getSacCost()) { return false; } if (abCost.getLifeCost()) { if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) return false; } if (abCost.getDiscardCost()) return false; if (abCost.getSubCounter()) { if (abCost.getCounterType().equals(Counters.P1P1)) return false; // Other counters should be used } } boolean humanHasHand = AllZoneUtil.getCardsInZone(Constant.Zone.Hand, AllZone.getHumanPlayer()).size() > 0; if (tgt != null) { if (!humanHasHand) return false; discardTargetAI(af); } else { // TODO: Add appropriate restrictions ArrayList<Player> players = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); if (players.size() == 1) { if (players.get(0).isComputer()) { // the ai should only be using something like this if he has few cards in hand, // cards like this better have a good drawback to be in the AIs deck } else { // defined to the human, so that's fine as long the human has cards if (!humanHasHand) return false; } } else { // Both players discard, any restrictions? } } if (!params.get("Mode").equals("Hand") && !params.get("Mode").equals("RevealDiscardAll")) { if (params.get("NumCards").equals("X") && source.getSVar("X").equals("Count$xPaid")) { // Set PayX here to maximum value. int cardsToDiscard = Math.min(ComputerUtil.determineLeftoverMana(sa), AllZoneUtil.getCardsInZone(Constant.Zone.Hand, AllZone.getHumanPlayer()).size()); source.setSVar("PayX", Integer.toString(cardsToDiscard)); } } //Don't use draw abilities before main 2 if possible if (AllZone.getPhase().isBefore(Constant.Phase.Main2) && !params.containsKey("ActivatingPhases")) return false; //Don't tap creatures that may be able to block if (AbilityFactory.waitForBlocking(sa)) return false; double chance = .5; // 50 percent chance of discarding with instant speed stuff if (AbilityFactory.isSorcerySpeed(sa)) chance = .75; // 75% chance for sorcery speed if ((AllZone.getPhase().is(Constant.Phase.End_Of_Turn) && AllZone.getPhase().isNextTurn(AllZone.getComputerPlayer()))) chance = .9; // 90% for end of opponents turn Random r = MyRandom.random; boolean randomReturn = r.nextFloat() <= Math.pow(chance, source.getAbilityUsed() + 1); if (AbilityFactory.playReusable(sa)) randomReturn = true; // some other variables here, like handsize vs. maxHandSize Ability_Sub subAb = sa.getSubAbility(); if (subAb != null) randomReturn &= subAb.chkAI_Drawback(); return randomReturn; }//discardCanPlayAI() /** * <p>discardTargetAI.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a boolean. */ private static boolean discardTargetAI(AbilityFactory af) { Target tgt = af.getAbTgt(); if (tgt != null) { if (AllZone.getHumanPlayer().canTarget(af.getHostCard())) { tgt.addTarget(AllZone.getHumanPlayer()); return true; } } return false; }// discardTargetAI() /** * <p>discardTrigger.</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 discardTrigger(AbilityFactory af, SpellAbility sa, boolean mandatory) { if (!ComputerUtil.canPayCost(sa)) return false; Target tgt = af.getAbTgt(); if (tgt != null) { if (!discardTargetAI(af)) { if (mandatory && AllZone.getComputerPlayer().canTarget(af.getHostCard())) tgt.addTarget(AllZone.getComputerPlayer()); else return false; } } return true; }// discardTrigger() /** * <p>discardCheckDrawbackAI.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param subAb a {@link forge.card.spellability.Ability_Sub} object. * @return a boolean. */ private static boolean discardCheckDrawbackAI(AbilityFactory af, Ability_Sub subAb) { // Drawback AI improvements // if parent draws cards, make sure cards in hand + cards drawn > 0 Target tgt = af.getAbTgt(); if (tgt != null) { discardTargetAI(af); } // TODO: check for some extra things return true; }// discardCheckDrawbackAI() //********************************************************************** //******************************* Shuffle ****************************** //********************************************************************** /** * <p>createAbilityShuffle.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createAbilityShuffle(final AbilityFactory af) { final SpellAbility abShuffle = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = -1245185178904838198L; @Override public String getStackDescription() { return shuffleStackDescription(af, this); } @Override public boolean canPlayAI() { return shuffleCanPlayAI(af, this); } @Override public void resolve() { shuffleResolve(af, this); } @Override public boolean doTrigger(boolean mandatory) { return shuffleTrigger(af, this, mandatory); } }; return abShuffle; } /** * <p>createSpellShuffle.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createSpellShuffle(final AbilityFactory af) { final SpellAbility spShuffle = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = 589035800601547559L; @Override public String getStackDescription() { return shuffleStackDescription(af, this); } @Override public boolean canPlayAI() { return shuffleCanPlayAI(af, this); } @Override public void resolve() { shuffleResolve(af, this); } }; return spShuffle; } /** * <p>createDrawbackShuffle.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createDrawbackShuffle(final AbilityFactory af) { final SpellAbility dbShuffle = new Ability_Sub(af.getHostCard(), af.getAbTgt()) { private static final long serialVersionUID = 5974307947494280639L; @Override public String getStackDescription() { return shuffleStackDescription(af, this); } @Override public void resolve() { shuffleResolve(af, this); } @Override public boolean chkAI_Drawback() { return shuffleTargetAI(af, this, false, false); } @Override public boolean doTrigger(boolean mandatory) { return shuffleTrigger(af, this, mandatory); } }; return dbShuffle; } /** * <p>shuffleStackDescription.</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 shuffleStackDescription(AbilityFactory af, SpellAbility sa) { HashMap<String, String> params = af.getMapParams(); StringBuilder sb = new StringBuilder(); if (!(sa instanceof Ability_Sub)) sb.append(sa.getSourceCard().getName()).append(" - "); else sb.append(" "); String conditionDesc = params.get("ConditionDescription"); if (conditionDesc != null) sb.append(conditionDesc).append(" "); ArrayList<Player> tgtPlayers; Target tgt = af.getAbTgt(); if (tgt != null) tgtPlayers = tgt.getTargetPlayers(); else tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); if (tgtPlayers.size() > 0) { Iterator<Player> it = tgtPlayers.iterator(); while (it.hasNext()) { sb.append(it.next().getName()); if (it.hasNext()) sb.append(" and "); } } else { sb.append("Error - no target players for Shuffle. "); } sb.append(" shuffle"); if (tgtPlayers.size() > 1) sb.append(" their libraries"); else sb.append("s his or her library"); sb.append("."); Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { sb.append(abSub.getStackDescription()); } return sb.toString(); } /** * <p>shuffleCanPlayAI.</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 shuffleCanPlayAI(final AbilityFactory af, SpellAbility sa) { //not really sure when the compy would use this; maybe only after a human // deliberately put a card on top of their library return false; /* if (!ComputerUtil.canPayCost(sa)) return false; Card source = sa.getSourceCard(); Random r = MyRandom.random; boolean randomReturn = r.nextFloat() <= Math.pow(.667, source.getAbilityUsed()+1); if (AbilityFactory.playReusable(sa)) randomReturn = true; Ability_Sub subAb = sa.getSubAbility(); if (subAb != null) randomReturn &= subAb.chkAI_Drawback(); return randomReturn; */ } /** * <p>shuffleTargetAI.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param primarySA a boolean. * @param mandatory a boolean. * @return a boolean. */ private static boolean shuffleTargetAI(AbilityFactory af, SpellAbility sa, boolean primarySA, boolean mandatory) { return false; }// shuffleTargetAI() /** * <p>shuffleTrigger.</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 shuffleTrigger(AbilityFactory af, SpellAbility sa, boolean mandatory) { if (!ComputerUtil.canPayCost(sa)) // If there is a cost payment return false; if (!shuffleTargetAI(af, sa, false, mandatory)) return false; // check SubAbilities DoTrigger? Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { return abSub.doTrigger(mandatory); } return true; } /** * <p>shuffleResolve.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. */ private static void shuffleResolve(final AbilityFactory af, final SpellAbility sa) { Card host = af.getHostCard(); HashMap<String, String> params = af.getMapParams(); boolean optional = params.containsKey("Optional"); ArrayList<Player> tgtPlayers; Target tgt = af.getAbTgt(); if (tgt != null) tgtPlayers = tgt.getTargetPlayers(); else tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); for (Player p : tgtPlayers) { if (tgt == null || p.canTarget(af.getHostCard())) { if (optional && sa.getActivatingPlayer().isHuman() && !GameActionUtil.showYesNoDialog(host, "Have " + p + " shuffle?")) { ; //do nothing } else { p.shuffle(); } } } } }//end class AbilityFactory_ZoneAffecting