package forge.card.spellability; import forge.*; import forge.card.cardFactory.CardFactoryUtil; import forge.gui.GuiUtils; import forge.gui.input.Input; import java.util.ArrayList; import java.util.HashMap; /** * <p>Target_Selection class.</p> * * @author Forge * @version $Id: $ */ public class Target_Selection { private Target target = null; private SpellAbility ability = null; private Card card = null; /** * <p>getTgt.</p> * * @return a {@link forge.card.spellability.Target} object. */ public Target getTgt() { return target; } /** * <p>Getter for the field <code>ability</code>.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getAbility() { return ability; } /** * <p>Getter for the field <code>card</code>.</p> * * @return a {@link forge.Card} object. */ public Card getCard() { return card; } private SpellAbility_Requirements req = null; /** * <p>setRequirements.</p> * * @param reqs a {@link forge.card.spellability.SpellAbility_Requirements} object. */ public void setRequirements(SpellAbility_Requirements reqs) { req = reqs; } private boolean bCancel = false; /** * <p>setCancel.</p> * * @param done a boolean. */ public void setCancel(boolean done) { bCancel = done; } /** * <p>isCanceled.</p> * * @return a boolean. */ public boolean isCanceled() { return bCancel; } private boolean bDoneTarget = false; /** * <p>setDoneTarget.</p> * * @param done a boolean. */ public void setDoneTarget(boolean done) { bDoneTarget = done; } /** * <p>Constructor for Target_Selection.</p> * * @param tgt a {@link forge.card.spellability.Target} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. */ public Target_Selection(Target tgt, SpellAbility sa) { target = tgt; ability = sa; card = sa.getSourceCard(); } /** * <p>doesTarget.</p> * * @return a boolean. */ public boolean doesTarget() { if (target == null) return false; return target.doesTarget(); } /** * <p>resetTargets.</p> */ public void resetTargets() { if (target != null) target.resetTargets(); } /** * <p>chooseTargets.</p> * * @return a boolean. */ public boolean chooseTargets() { // if not enough targets chosen, reset and cancel Ability if (bCancel || (bDoneTarget && !target.isMinTargetsChosen(card, ability))) { bCancel = true; req.finishedTargeting(); return false; } else if (!doesTarget() || bDoneTarget && target.isMinTargetsChosen(card, ability) || target.isMaxTargetsChosen(card, ability)) { Ability_Sub abSub = ability.getSubAbility(); if (abSub == null) { // if no more SubAbilities finish targeting req.finishedTargeting(); return true; } else { // Has Sub Ability Target_Selection ts = new Target_Selection(abSub.getTarget(), abSub); ts.setRequirements(req); ts.resetTargets(); return ts.chooseTargets(); } } chooseValidInput(); return false; } // these have been copied over from CardFactoryUtil as they need two extra parameters for target selection. // however, due to the changes necessary for SA_Requirements this is much different than the original /** * <p>chooseValidInput.</p> */ public void chooseValidInput() { Target tgt = this.getTgt(); String zone = tgt.getZone(); final boolean mandatory = target.getMandatory() ? target.hasCandidates() : false; if (zone.equals(Constant.Zone.Stack)) { // If Zone is Stack, the choices are handled slightly differently chooseCardFromStack(mandatory); return; } CardList choices = AllZoneUtil.getCardsInZone(zone).getValidCards(target.getValidTgts(), ability.getActivatingPlayer(), ability.getSourceCard()); // Remove cards already targeted ArrayList<Card> targeted = tgt.getTargetCards(); for (Card c : targeted) { if (choices.contains(c)) choices.remove(c); } if (zone.equals(Constant.Zone.Battlefield)) { AllZone.getInputControl().setInput(input_targetSpecific(choices, true, mandatory)); } else chooseCardFromList(choices, true, mandatory); }//input_targetValid //CardList choices are the only cards the user can successful select /** * <p>input_targetSpecific.</p> * * @param choices a {@link forge.CardList} object. * @param targeted a boolean. * @param mandatory a boolean. * @return a {@link forge.gui.input.Input} object. */ public Input input_targetSpecific(final CardList choices, final boolean targeted, final boolean mandatory) { final SpellAbility sa = this.ability; final Target_Selection select = this; final Target tgt = this.target; final SpellAbility_Requirements req = this.req; Input target = new Input() { private static final long serialVersionUID = -1091595663541356356L; @Override public void showMessage() { StringBuilder sb = new StringBuilder(); sb.append("Targeted: "); sb.append(tgt.getTargetedString()); sb.append("\n"); sb.append(tgt.getVTSelection()); AllZone.getDisplay().showMessage(sb.toString()); // If reached Minimum targets, enable OK button if (!tgt.isMinTargetsChosen(sa.getSourceCard(), sa)) ButtonUtil.enableOnlyCancel(); else ButtonUtil.enableAll(); if (mandatory) ButtonUtil.disableCancel(); } @Override public void selectButtonCancel() { select.setCancel(true); stop(); req.finishedTargeting(); } @Override public void selectButtonOK() { select.setDoneTarget(true); done(); } @Override public void selectCard(Card card, PlayerZone zone) { // leave this in temporarily, there some seriously wrong things going on here if (targeted && !CardFactoryUtil.canTarget(sa, card)) { AllZone.getDisplay().showMessage("Cannot target this card (Shroud? Protection? Restrictions?)."); } else if (choices.contains(card)) { tgt.addTarget(card); done(); } }//selectCard() @Override public void selectPlayer(Player player) { if ((tgt.canTgtPlayer() || (tgt.canOnlyTgtOpponent() && player.equals(sa.getActivatingPlayer().getOpponent()))) && player.canTarget(sa.getSourceCard())) { tgt.addTarget(player); done(); } } void done() { stop(); select.chooseTargets(); } }; return target; }//input_targetSpecific() /** * <p>chooseCardFromList.</p> * * @param choices a {@link forge.CardList} object. * @param targeted a boolean. * @param mandatory a boolean. */ public void chooseCardFromList(final CardList choices, boolean targeted, final boolean mandatory) { // Send in a list of valid cards, and popup a choice box to target final Card dummy = new Card(); dummy.setName("[FINISH TARGETING]"); final SpellAbility sa = this.ability; final String message = this.target.getVTSelection(); Target tgt = this.getTgt(); CardList choicesWithDone = choices; if (tgt.isMinTargetsChosen(sa.getSourceCard(), sa)) { // is there a more elegant way of doing this? choicesWithDone.add(dummy); } Object check = GuiUtils.getChoiceOptional(message, choicesWithDone.toArray()); if (check != null) { Card c = (Card) check; if (c.equals(dummy)) this.setDoneTarget(true); else tgt.addTarget(c); } else this.setCancel(true); this.chooseTargets(); } /** * <p>chooseCardFromStack.</p> * * @param mandatory a boolean. */ public void chooseCardFromStack(final boolean mandatory) { Target tgt = this.target; String message = tgt.getVTSelection(); Target_Selection select = this; // Find what's targetable, then allow human to choose ArrayList<SpellAbility> choosables = getTargetableOnStack(this.ability, select.getTgt()); HashMap<String, SpellAbility> map = new HashMap<String, SpellAbility>(); for (SpellAbility sa : choosables) { map.put(sa.getStackDescription(), sa); } String[] choices = new String[map.keySet().size()]; choices = map.keySet().toArray(choices); if (choices.length == 0) { select.setCancel(true); } else { String madeChoice = GuiUtils.getChoiceOptional(message, choices); if (madeChoice != null) { tgt.addTarget(map.get(madeChoice)); } else select.setCancel(true); } select.chooseTargets(); } // TODO: The following three functions are Utility functions for TargetOnStack, probably should be moved // The following should be select.getTargetableOnStack() /** * <p>getTargetableOnStack.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param tgt a {@link forge.card.spellability.Target} object. * @return a {@link java.util.ArrayList} object. */ public static ArrayList<SpellAbility> getTargetableOnStack(SpellAbility sa, Target tgt) { ArrayList<SpellAbility> choosables = new ArrayList<SpellAbility>(); for (int i = 0; i < AllZone.getStack().size(); i++) { choosables.add(AllZone.getStack().peekAbility(i)); } for (int i = 0; i < choosables.size(); i++) { if (!matchSpellAbility(sa, choosables.get(i), tgt)) { choosables.remove(i); } } return choosables; } /** * <p>matchSpellAbility.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param topSA a {@link forge.card.spellability.SpellAbility} object. * @param tgt a {@link forge.card.spellability.Target} object. * @return a boolean. */ public static boolean matchSpellAbility(SpellAbility sa, SpellAbility topSA, Target tgt) { String saType = tgt.getTargetSpellAbilityType(); if (null == saType) { //just take this to mean no restrictions - carry on. } else if (topSA.isSpell()) { if (!saType.contains("Spell")) return false; } else if (topSA.isTrigger()) { if (!saType.contains("Triggered")) return false; } else if (topSA.isAbility()) { if (!saType.contains("Activated")) return false; } String splitTargetRestrictions = tgt.getSAValidTargeting(); if (splitTargetRestrictions != null) { // TODO: What about spells with SubAbilities with Targets? Target matchTgt = topSA.getTarget(); if (matchTgt == null) return false; boolean result = false; for (Object o : matchTgt.getTargets()) { if (matchesValid(o, splitTargetRestrictions.split(","), sa)) { result = true; break; } } if (!result) return false; } if (!matchesValid(topSA, tgt.getValidTgts(), sa)) { return false; } return true; } /** * <p>matchesValid.</p> * * @param o a {@link java.lang.Object} object. * @param valids an array of {@link java.lang.String} objects. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private static boolean matchesValid(Object o, String[] valids, SpellAbility sa) { Card srcCard = sa.getSourceCard(); Player activatingPlayer = sa.getActivatingPlayer(); if (o instanceof Card) { Card c = (Card) o; return c.isValidCard(valids, activatingPlayer, srcCard); } if (o instanceof Player) { for (String v : valids) { if (v.equalsIgnoreCase("Player")) return true; if (v.equalsIgnoreCase("Opponent")) { if (o.equals(activatingPlayer.getOpponent())) { return true; } } if (v.equalsIgnoreCase("You")) return o.equals(activatingPlayer); } } if (o instanceof SpellAbility) { Card c = ((SpellAbility) o).getSourceCard(); return c.isValidCard(valids, activatingPlayer, srcCard); } return false; } }