package forge.card.spellability; import forge.*; import forge.card.abilityFactory.AbilityFactory; import forge.card.mana.Mana; import forge.gui.input.Input; import java.util.ArrayList; import java.util.HashMap; //only SpellAbility can go on the stack //override any methods as needed /** * <p>Abstract SpellAbility class.</p> * * @author Forge * @version $Id: $ */ public abstract class SpellAbility { public Object[] choices_made; //open ended Casting choice storage //choices for constructor isPermanent argument /** Constant <code>Spell=0</code> */ public static final int Spell = 0; /** Constant <code>Ability=1</code> */ public static final int Ability = 1; private String description = ""; private Player targetPlayer = null; private String stackDescription = ""; private String manaCost = ""; private String additionalManaCost = ""; private String multiKickerManaCost = ""; private String replicateManaCost = ""; private String xManaCost = ""; private Player activatingPlayer = null; private String type = "Intrinsic"; //set to Intrinsic by default private Card targetCard; private Card sourceCard; private CardList targetList; // targetList doesn't appear to be used anymore private boolean spell; private boolean trigger = false; private int sourceTrigger = -1; private boolean mandatory = false; private boolean tapAbility; private boolean untapAbility; private boolean buyBackAbility = false; //false by default private boolean flashBackAbility = false; private boolean multiKicker = false; private boolean replicate = false; private boolean xCost = false; private boolean kickerAbility = false; private boolean kothThirdAbility = false; private boolean cycling = false; private Input beforePayMana; private Input afterResolve; private Input afterPayMana; protected Cost payCosts = null; protected Target chosenTarget = null; private SpellAbility_Restriction restrictions = new SpellAbility_Restriction(); private SpellAbility_Condition conditions = new SpellAbility_Condition(); private Ability_Sub subAbility = null; private AbilityFactory abilityFactory = null; private ArrayList<Mana> payingMana = new ArrayList<Mana>(); private ArrayList<Ability_Mana> paidAbilities = new ArrayList<Ability_Mana>(); private HashMap<String, CardList> paidLists = new HashMap<String, CardList>(); private HashMap<String, Object> triggeringObjects = new HashMap<String, Object>(); private Command cancelCommand = Command.Blank; private Command beforePayManaAI = Command.Blank; private CommandArgs randomTarget = new CommandArgs() { private static final long serialVersionUID = 1795025064923737374L; public void execute(Object o) { } }; /** * <p>Constructor for SpellAbility.</p> * * @param spellOrAbility a int. * @param i_sourceCard a {@link forge.Card} object. */ public SpellAbility(int spellOrAbility, Card i_sourceCard) { if (spellOrAbility == Spell) spell = true; else if (spellOrAbility == Ability) spell = false; else throw new RuntimeException("SpellAbility : constructor error, invalid spellOrAbility argument = " + spellOrAbility); sourceCard = i_sourceCard; } //Spell, and Ability, and other Ability objects override this method /** * <p>canPlay.</p> * * @return a boolean. */ abstract public boolean canPlay(); //all Spell's and Abilities must override this method /** * <p>resolve.</p> */ abstract public void resolve(); /** * <p>canPlayAI.</p> * * @return a boolean. */ public boolean canPlayAI() { return true; } // This should be overridden by ALL AFs /** * <p>doTrigger.</p> * * @param mandatory a boolean. * @return a boolean. */ public boolean doTrigger(boolean mandatory) { return false; } /** * <p>chooseTargetAI.</p> */ public void chooseTargetAI() { randomTarget.execute(this); } /** * <p>setChooseTargetAI.</p> * * @param c a {@link forge.CommandArgs} object. */ public void setChooseTargetAI(CommandArgs c) { randomTarget = c; } /** * <p>getChooseTargetAI.</p> * * @return a {@link forge.CommandArgs} object. */ public CommandArgs getChooseTargetAI() { return randomTarget; } /** * <p>Getter for the field <code>manaCost</code>.</p> * * @return a {@link java.lang.String} object. */ public String getManaCost() { return manaCost; } /** * <p>Setter for the field <code>manaCost</code>.</p> * * @param cost a {@link java.lang.String} object. */ public void setManaCost(String cost) { manaCost = cost; } /** * <p>Getter for the field <code>additionalManaCost</code>.</p> * * @return a {@link java.lang.String} object. */ public String getAdditionalManaCost() { return additionalManaCost; } /** * <p>Setter for the field <code>additionalManaCost</code>.</p> * * @param cost a {@link java.lang.String} object. */ public void setAdditionalManaCost(String cost) { additionalManaCost = cost; } /** * <p>Getter for the field <code>multiKickerManaCost</code>.</p> * * @return a {@link java.lang.String} object. */ public String getMultiKickerManaCost() { return multiKickerManaCost; } /** * <p>Setter for the field <code>multiKickerManaCost</code>.</p> * * @param cost a {@link java.lang.String} object. */ public void setMultiKickerManaCost(String cost) { multiKickerManaCost = cost; } /** * <p>Getter for the field <code>replicateManaCost</code>.</p> * * @return a {@link java.lang.String} object. */ public String getReplicateManaCost() { return replicateManaCost; } /** * <p>Setter for the field <code>replicateManaCost</code>.</p> * * @param cost a {@link java.lang.String} object. */ public void setReplicateManaCost(String cost) { replicateManaCost = cost; } /** * <p>Getter for the field <code>xManaCost</code>.</p> * * @return a {@link java.lang.String} object. */ public String getXManaCost() { return xManaCost; } /** * <p>Setter for the field <code>xManaCost</code>.</p> * * @param cost a {@link java.lang.String} object. */ public void setXManaCost(String cost) { xManaCost = cost; } /** * <p>Getter for the field <code>activatingPlayer</code>.</p> * * @return a {@link forge.Player} object. */ public Player getActivatingPlayer() { return activatingPlayer; } /** * <p>Setter for the field <code>activatingPlayer</code>.</p> * * @param player a {@link forge.Player} object. */ public void setActivatingPlayer(Player player) { // trickle down activating player activatingPlayer = player; if (subAbility != null) subAbility.setActivatingPlayer(player); } /** * <p>isSpell.</p> * * @return a boolean. */ public boolean isSpell() { return spell; } /** * <p>isAbility.</p> * * @return a boolean. */ public boolean isAbility() { return !isSpell(); } /** * <p>isTapAbility.</p> * * @return a boolean. */ public boolean isTapAbility() { return tapAbility; } /** * <p>isUntapAbility.</p> * * @return a boolean. */ public boolean isUntapAbility() { return untapAbility; } /** * <p>makeUntapAbility.</p> */ public void makeUntapAbility() { untapAbility = true; tapAbility = false; } /** * <p>setIsBuyBackAbility.</p> * * @param b a boolean. */ public void setIsBuyBackAbility(boolean b) { buyBackAbility = b; } /** * <p>isBuyBackAbility.</p> * * @return a boolean. */ public boolean isBuyBackAbility() { return buyBackAbility; } /** * <p>setIsMultiKicker.</p> * * @param b a boolean. */ public void setIsMultiKicker(boolean b) { multiKicker = b; } /** * <p>isMultiKicker.</p> * * @return a boolean. */ public boolean isMultiKicker() { return multiKicker; } /** * <p>setIsReplicate.</p> * * @param b a boolean. */ public void setIsReplicate(boolean b) { replicate = b; } /** * <p>isReplicate.</p> * * @return a boolean. */ public boolean isReplicate() { return replicate; } /** * <p>setIsXCost.</p> * * @param b a boolean. */ public void setIsXCost(boolean b) { xCost = b; } /** * <p>isXCost.</p> * * @return a boolean. */ public boolean isXCost() { return xCost; } /** * <p>setIsCycling.</p> * * @param b a boolean. */ public void setIsCycling(boolean b) { cycling = b; } /** * <p>isCycling.</p> * * @return a boolean. */ public boolean isCycling() { return cycling; } /** * <p>Setter for the field <code>sourceCard</code>.</p> * * @param c a {@link forge.Card} object. */ public void setSourceCard(Card c) { sourceCard = c; } /** * <p>Getter for the field <code>sourceCard</code>.</p> * * @return a {@link forge.Card} object. */ public Card getSourceCard() { return sourceCard; } /** * <p>Getter for the field <code>beforePayManaAI</code>.</p> * * @return a {@link forge.Command} object. */ public Command getBeforePayManaAI() { return beforePayManaAI; } /** * <p>Setter for the field <code>beforePayManaAI</code>.</p> * * @param c a {@link forge.Command} object. */ public void setBeforePayManaAI(Command c) { beforePayManaAI = c; } //begin - Input methods /** * <p>Getter for the field <code>beforePayMana</code>.</p> * * @return a {@link forge.gui.input.Input} object. */ public Input getBeforePayMana() { return beforePayMana; } /** * <p>Setter for the field <code>beforePayMana</code>.</p> * * @param in a {@link forge.gui.input.Input} object. */ public void setBeforePayMana(Input in) { beforePayMana = in; } /** * <p>Getter for the field <code>afterPayMana</code>.</p> * * @return a {@link forge.gui.input.Input} object. */ public Input getAfterPayMana() { return afterPayMana; } /** * <p>Setter for the field <code>afterPayMana</code>.</p> * * @param in a {@link forge.gui.input.Input} object. */ public void setAfterPayMana(Input in) { afterPayMana = in; } /** * <p>Getter for the field <code>payCosts</code>.</p> * * @return a {@link forge.card.spellability.Cost} object. */ public Cost getPayCosts() { return payCosts; } /** * <p>Setter for the field <code>payCosts</code>.</p> * * @param abCost a {@link forge.card.spellability.Cost} object. */ public void setPayCosts(Cost abCost) { payCosts = abCost; } /** * <p>getTarget.</p> * * @return a {@link forge.card.spellability.Target} object. */ public Target getTarget() { return chosenTarget; } /** * <p>setTarget.</p> * * @param tgt a {@link forge.card.spellability.Target} object. */ public void setTarget(Target tgt) { chosenTarget = tgt; } /** * <p>Setter for the field <code>restrictions</code>.</p> * * @param restrict a {@link forge.card.spellability.SpellAbility_Restriction} object. */ public void setRestrictions(SpellAbility_Restriction restrict) { restrictions = restrict; } /** * <p>Getter for the field <code>restrictions</code>.</p> * * @return a {@link forge.card.spellability.SpellAbility_Restriction} object. */ public SpellAbility_Restriction getRestrictions() { return restrictions; } /** * <p>Setter for the field <code>conditions</code>.</p> * * @param condition a {@link forge.card.spellability.SpellAbility_Condition} object. * @since 1.0.15 */ public void setConditions(SpellAbility_Condition condition) { conditions = condition; } /** * <p>Getter for the field <code>conditions</code>.</p> * * @return a {@link forge.card.spellability.SpellAbility_Condition} object. * @since 1.0.15 */ public SpellAbility_Condition getConditions() { return conditions; } /** * <p>Setter for the field <code>abilityFactory</code>.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. */ public void setAbilityFactory(AbilityFactory af) { abilityFactory = af; } /** * <p>Getter for the field <code>abilityFactory</code>.</p> * * @return a {@link forge.card.abilityFactory.AbilityFactory} object. */ public AbilityFactory getAbilityFactory() { return abilityFactory; } /** * <p>Getter for the field <code>payingMana</code>.</p> * * @return a {@link java.util.ArrayList} object. */ public ArrayList<Mana> getPayingMana() { return payingMana; } /** * <p>getPayingManaAbilities.</p> * * @return a {@link java.util.ArrayList} object. */ public ArrayList<Ability_Mana> getPayingManaAbilities() { return paidAbilities; } // Combined PaidLists /** * <p>setPaidHash.</p> * * @param hash a {@link java.util.HashMap} object. */ public void setPaidHash(HashMap<String, CardList> hash) { paidLists = hash; } /** * <p>getPaidHash.</p> * * @return a {@link java.util.HashMap} object. */ public HashMap<String, CardList> getPaidHash() { return paidLists; } // Paid List are for things ca /** * <p>setPaidList.</p> * * @param list a {@link forge.CardList} object. * @param str a {@link java.lang.String} object. */ public void setPaidList(CardList list, String str) { paidLists.put(str, list); } /** * <p>getPaidList.</p> * * @param str a {@link java.lang.String} object. * @return a {@link forge.CardList} object. */ public CardList getPaidList(String str) { return paidLists.get(str); } /** * <p>addCostToHashList.</p> * * @param c a {@link forge.Card} object. * @param str a {@link java.lang.String} object. */ public void addCostToHashList(Card c, String str) { if (!paidLists.containsKey(str)) paidLists.put(str, new CardList()); paidLists.get(str).add(c); } /** * <p>resetPaidHash.</p> */ public void resetPaidHash() { paidLists = new HashMap<String, CardList>(); } /** * <p>Getter for the field <code>triggeringObjects</code>.</p> * * @return a {@link java.util.HashMap} object. * @since 1.0.15 */ public HashMap<String, Object> getTriggeringObjects() { return triggeringObjects; } /** * <p>setAllTriggeringObjects.</p> * * @param triggeredObjects a {@link java.util.HashMap} object. * @since 1.0.15 */ public void setAllTriggeringObjects(HashMap<String, Object> triggeredObjects) { this.triggeringObjects = triggeredObjects; } /** * <p>setTriggeringObject.</p> * * @param type a {@link java.lang.String} object. * @param o a {@link java.lang.Object} object. * @since 1.0.15 */ public void setTriggeringObject(String type, Object o) { this.triggeringObjects.put(type, o); } /** * <p>getTriggeringObject.</p> * * @param type a {@link java.lang.String} object. * @return a {@link java.lang.Object} object. * @since 1.0.15 */ public Object getTriggeringObject(String type) { return triggeringObjects.get(type); } /** * <p>hasTriggeringObject.</p> * * @param type a {@link java.lang.String} object. * @return a boolean. * @since 1.0.15 */ public boolean hasTriggeringObject(String type) { return triggeringObjects.containsKey(type); } /** * <p>resetTriggeringObjects.</p> * * @since 1.0.15 */ public void resetTriggeringObjects() { triggeringObjects = new HashMap<String, Object>(); } /** * <p>resetOnceResolved.</p> */ public void resetOnceResolved() { resetPaidHash(); if (chosenTarget != null) chosenTarget.resetTargets(); resetTriggeringObjects(); } /** * <p>Getter for the field <code>afterResolve</code>.</p> * * @return a {@link forge.gui.input.Input} object. */ public Input getAfterResolve() { return afterResolve; } /** * <p>Setter for the field <code>afterResolve</code>.</p> * * @param in a {@link forge.gui.input.Input} object. */ public void setAfterResolve(Input in) { afterResolve = in; } /** * <p>Setter for the field <code>stackDescription</code>.</p> * * @param s a {@link java.lang.String} object. */ public void setStackDescription(String s) { stackDescription = s; if (description == "" && sourceCard.getText().equals("")) description = s; } /** * <p>Getter for the field <code>stackDescription</code>.</p> * * @return a {@link java.lang.String} object. */ public String getStackDescription() { if (stackDescription.equals(getSourceCard().getText().trim())) return getSourceCard().getName() + " - " + getSourceCard().getText(); return stackDescription.replaceAll("CARDNAME", this.getSourceCard().getName()); } /** * <p>isIntrinsic.</p> * * @return a boolean. */ public boolean isIntrinsic() { return type.equals("Intrinsic"); } /** * <p>isExtrinsic.</p> * * @return a boolean. */ public boolean isExtrinsic() { return type.equals("Extrinsic"); } /** * <p>Setter for the field <code>type</code>.</p> * * @param s a {@link java.lang.String} object. */ public void setType(String s) //Extrinsic or Intrinsic: { type = s; } /** * <p>Getter for the field <code>type</code>.</p> * * @return a {@link java.lang.String} object. */ public String getType() //Extrinsic or Intrinsic: { return type; } //setDescription() includes mana cost and everything like //"G, tap: put target creature from your hand onto the battlefield" /** * <p>Setter for the field <code>description</code>.</p> * * @param s a {@link java.lang.String} object. */ public void setDescription(String s) { description = s; } /** * <p>Getter for the field <code>description</code>.</p> * * @return a {@link java.lang.String} object. */ public String getDescription() { return description; } /** {@inheritDoc} */ @Override public String toString() { StringBuilder sb = new StringBuilder(); SpellAbility node = this; while (node != null) { if (node != this) sb.append(" "); sb.append(node.getDescription().replace("CARDNAME", node.getSourceCard().getName())); node = node.getSubAbility(); } return sb.toString(); } /** * <p>Setter for the field <code>subAbility</code>.</p> * * @param subAbility a {@link forge.card.spellability.Ability_Sub} object. */ public void setSubAbility(Ability_Sub subAbility) { this.subAbility = subAbility; if (subAbility != null) subAbility.setParent(this); } /** * <p>Getter for the field <code>subAbility</code>.</p> * * @return a {@link forge.card.spellability.Ability_Sub} object. */ public Ability_Sub getSubAbility() { return this.subAbility; } /** * <p>Getter for the field <code>targetCard</code>.</p> * * @return a {@link forge.Card} object. */ public Card getTargetCard() { if (targetCard == null) { Target tgt = this.getTarget(); if (tgt != null) { ArrayList<Card> list = tgt.getTargetCards(); if (!list.isEmpty()) return list.get(0); } return null; } return targetCard; } /** * <p>Setter for the field <code>targetCard</code>.</p> * * @param card a {@link forge.Card} object. */ public void setTargetCard(Card card) { if (card == null) { System.out.println(getSourceCard() + " - SpellAbility.setTargetCard() called with null for target card."); return; } Target tgt = this.getTarget(); if (tgt != null) { tgt.addTarget(card); } else { targetPlayer = null;//reset setTargetPlayer() targetCard = card; } String desc = ""; if (null != card) { if (!card.isFaceDown()) desc = getSourceCard().getName() + " - targeting " + card; else desc = getSourceCard().getName() + " - targeting Morph(" + card.getUniqueNumber() + ")"; setStackDescription(desc); } } /** * <p>Getter for the field <code>targetList</code>.</p> * * @return a {@link forge.CardList} object. */ public CardList getTargetList() { return targetList; } /** * <p>Setter for the field <code>targetList</code>.</p> * * @param list a {@link forge.CardList} object. */ public void setTargetList(CardList list) { // The line below started to create a null error at forge.CardFactoryUtil.canTarget(CardFactoryUtil.java:3329) // after ForgeSVN r2699. I hope that commenting out the line below will not result in other bugs. :) // targetPlayer = null;//reset setTargetPlayer() targetList = list; StringBuilder sb = new StringBuilder(); sb.append(getSourceCard().getName()).append(" - targeting "); for (int i = 0; i < targetList.size(); i++) { if (!targetList.get(i).isFaceDown()) sb.append(targetList.get(i)); else sb.append("Morph(").append(targetList.get(i).getUniqueNumber()).append(")"); if (i < targetList.size() - 1) sb.append(", "); } setStackDescription(sb.toString()); } /** * <p>Setter for the field <code>targetPlayer</code>.</p> * * @param p a {@link forge.Player} object. */ public void setTargetPlayer(Player p) { if (p == null || (!(p.isHuman() || p.isComputer()))) throw new RuntimeException( "SpellAbility : setTargetPlayer() error, argument is " + p + " source card is " + getSourceCard()); Target tgt = this.getTarget(); if (tgt != null) { tgt.addTarget(p); } else { targetCard = null;//reset setTargetCard() targetPlayer = p; } setStackDescription(getSourceCard().getName() + " - targeting " + p); } /** * <p>Getter for the field <code>targetPlayer</code>.</p> * * @return a {@link forge.Player} object. */ public Player getTargetPlayer() { if (targetPlayer == null) { Target tgt = this.getTarget(); if (tgt != null) { ArrayList<Player> list = tgt.getTargetPlayers(); if (!list.isEmpty()) return list.get(0); } return null; } return targetPlayer; } /** * <p>Getter for the field <code>cancelCommand</code>.</p> * * @return a {@link forge.Command} object. */ public Command getCancelCommand() { return cancelCommand; } /** * <p>Setter for the field <code>cancelCommand</code>.</p> * * @param cancelCommand a {@link forge.Command} object. */ public void setCancelCommand(Command cancelCommand) { this.cancelCommand = cancelCommand; } /** * <p>Setter for the field <code>flashBackAbility</code>.</p> * * @param flashBackAbility a boolean. */ public void setFlashBackAbility(boolean flashBackAbility) { this.flashBackAbility = flashBackAbility; } /** * <p>isFlashBackAbility.</p> * * @return a boolean. */ public boolean isFlashBackAbility() { return flashBackAbility; } /** * <p>Setter for the field <code>kickerAbility</code>.</p> * * @param kab a boolean. */ public void setKickerAbility(boolean kab) { this.kickerAbility = kab; } /** * <p>isKickerAbility.</p> * * @return a boolean. */ public boolean isKickerAbility() { return kickerAbility; } // Only used by Ability_Reflected_Mana, because the user has an option to cancel the input. // Most spell abilities and even most mana abilities do not need to use this. /** * <p>wasCancelled.</p> * * @return a boolean. */ public boolean wasCancelled() { return false; } /** * <p>copy.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility copy() { SpellAbility clone = null; try { clone = (SpellAbility) this.clone(); } catch (CloneNotSupportedException e) { System.err.println(e); } return clone; } /** * <p>Setter for the field <code>kothThirdAbility</code>.</p> * * @param kothThirdAbility a boolean. */ public void setKothThirdAbility(boolean kothThirdAbility) { this.kothThirdAbility = kothThirdAbility; } /** * <p>isKothThirdAbility.</p> * * @return a boolean. */ public boolean isKothThirdAbility() { return kothThirdAbility; } /** * <p>Setter for the field <code>trigger</code>.</p> * * @param trigger a boolean. */ public void setTrigger(boolean trigger) { this.trigger = trigger; } /** * <p>isTrigger.</p> * * @return a boolean. */ public boolean isTrigger() { return trigger; } /** * <p>setSourceTrigger.</p> * * @param ID a int. */ public void setSourceTrigger(int ID) { sourceTrigger = ID; } /** * <p>getSourceTrigger.</p> * * @return a int. */ public int getSourceTrigger() { return sourceTrigger; } /** * <p>Setter for the field <code>mandatory</code>.</p> * * @param mand a boolean. */ public void setMandatory(boolean mand) { this.mandatory = mand; } /** * <p>isMandatory.</p> * * @return a boolean. */ public boolean isMandatory() { return mandatory; } /** * <p>getRootSpellAbility.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. * @since 1.0.15 */ public SpellAbility getRootSpellAbility() { if (this instanceof Ability_Sub) { SpellAbility parent = ((Ability_Sub) this).getParent(); if (parent != null) return parent.getRootSpellAbility(); } return this; } /** * <p>getAllTargetChoices.</p> * * @return a {@link java.util.ArrayList} object. * @since 1.0.15 */ public ArrayList<Target_Choices> getAllTargetChoices() { ArrayList<Target_Choices> res = new ArrayList<Target_Choices>(); SpellAbility sa = getRootSpellAbility(); if (sa.getTarget() != null) res.add(sa.getTarget().getTargetChoices()); while (sa.getSubAbility() != null) { sa = sa.getSubAbility(); if (sa.getTarget() != null) res.add(sa.getTarget().getTargetChoices()); } return res; } //is this a wrapping ability (used by trigger abilities) /** * <p>isWrapper.</p> * * @return a boolean. * @since 1.0.15 */ public boolean isWrapper() { return false; } }