From 1cdf7b3ef45c745602bd0f3c47d9496e18a8265d Mon Sep 17 00:00:00 2001 From: jendave Date: Sat, 6 Aug 2011 11:06:06 +0000 Subject: [PATCH] - Initial SubAbility work: DealDamage, GainLife, Draw, Mill all can now be called as a SubAbility and can call SubAbilities. - Target_Selection will now Target for Parent ability and each subAbility that requires it - Stack will fizzle if ALL Targets are illegal on resolution - Merged Orcish Cannonade, Psionic Entity to AF+SubAbility - Added Lunge - Target Prompt now Optional in AFs --- .gitattributes | 2 + res/cardsfolder/lunge.txt | 9 + res/cardsfolder/orcish_cannonade.txt | 7 +- res/cardsfolder/psionic_entity.txt | 3 +- src/forge/AbilityFactory.java | 101 ++- src/forge/AbilityFactory_AlterLife.java | 105 ++- src/forge/AbilityFactory_DealDamage.java | 683 +++++++++++--------- src/forge/AbilityFactory_ZoneAffecting.java | 192 ++++-- src/forge/Ability_Sub.java | 29 + src/forge/MagicStack.java | 63 +- src/forge/SpellAbility.java | 9 + src/forge/SpellAbility_Requirements.java | 7 +- src/forge/Target_Selection.java | 20 +- 13 files changed, 785 insertions(+), 445 deletions(-) create mode 100644 res/cardsfolder/lunge.txt create mode 100644 src/forge/Ability_Sub.java diff --git a/.gitattributes b/.gitattributes index 1e36a160820..b1e5c436ac0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2751,6 +2751,7 @@ res/cardsfolder/lull.txt -text svneol=native#text/plain res/cardsfolder/lumbering_satyr.txt -text svneol=native#text/plain res/cardsfolder/lumengrid_warden.txt -text svneol=native#text/plain res/cardsfolder/luminous_angel.txt -text svneol=native#text/plain +res/cardsfolder/lunge.txt -text svneol=native#text/plain res/cardsfolder/lure.txt -text svneol=native#text/plain res/cardsfolder/lurking_informant.txt -text svneol=native#text/plain res/cardsfolder/lurking_nightstalker.txt -text svneol=native#text/plain @@ -5790,6 +5791,7 @@ src/forge/Ability_Cost.java -text svneol=native#text/plain src/forge/Ability_Mana.java -text svneol=native#text/plain src/forge/Ability_Reflected_Mana.java svneol=native#text/plain src/forge/Ability_Static.java -text svneol=native#text/plain +src/forge/Ability_Sub.java -text svneol=native#text/plain src/forge/Ability_Tap.java svneol=native#text/plain src/forge/Ability_Triggered.java svneol=native#text/plain src/forge/AllZone.java svneol=native#text/plain diff --git a/res/cardsfolder/lunge.txt b/res/cardsfolder/lunge.txt new file mode 100644 index 00000000000..6fd18b3bf8e --- /dev/null +++ b/res/cardsfolder/lunge.txt @@ -0,0 +1,9 @@ +Name:Lunge +ManaCost:2 R +Types:Instant +Text:no text +A:SP$DealDamage | Cost$ 2 R | Tgt$ C | NumDmg$2 | SubAbility$SVar=DB1 | SpellDescription$ Lunge deals 2 damage to target creature and 2 damage to target player. +SVar:DB1:DB$DealDamage | NumDmg$ 2 | Tgt$ P +SVar:Rarity:Common +SVar:Picture:http://www.wizards.com/global/images/magic/general/lunge.jpg +End \ No newline at end of file diff --git a/res/cardsfolder/orcish_cannonade.txt b/res/cardsfolder/orcish_cannonade.txt index 7f99a15147b..97a3c3ed5eb 100644 --- a/res/cardsfolder/orcish_cannonade.txt +++ b/res/cardsfolder/orcish_cannonade.txt @@ -2,8 +2,9 @@ Name:Orcish Cannonade ManaCost:1 R R Types:Instant Text:no text -K:spDamageTgtCP:2:Drawback$DamageYou/3:Orcish Cannonade deals 2 damage to target creature or player and 3 damage to you. -K:Draw a card. +A:SP$DealDamage | Cost$ 1 R R | Tgt$ CP | NumDmg$2 | SubAbility$SVar=DB1 | SpellDescription$ Orcish Cannonade deals 2 damage to target creature or player and 3 damage to you. Draw a card. +SVar:DB1:DB$DealDamage | NumDmg$ 3 | Affected$ You | SubAbility$SVar=DB2 +SVar:DB2:DB$Draw | NumCards$ 1 SVar:Rarity:Common SVar:Picture:http://www.wizards.com/global/images/magic/general/orcish_cannonade.jpg -End +End \ No newline at end of file diff --git a/res/cardsfolder/psionic_entity.txt b/res/cardsfolder/psionic_entity.txt index 9ec0cc52fda..10908ba4e7d 100644 --- a/res/cardsfolder/psionic_entity.txt +++ b/res/cardsfolder/psionic_entity.txt @@ -3,7 +3,8 @@ ManaCost:4 U Types:Creature Illusion Text:no text PT:2/2 -K:abDamageTgtCP T:2:Drawback$DamageSelf/3:Psionic Entity deals 2 damage to target creature or player and 3 damage to itself.:Psionic Entity - deal damage to creature or player and itself. +A:AB$DealDamage | Cost$ T | Tgt$ CP | NumDmg$2 | SubAbility$SVar=DB1 | SpellDescription$ Psionic Entity deals 2 damage to target creature or player and 3 damage to itself. +SVar:DB1:DB$DealDamage | NumDmg$ 3 | Affected$ Self SVar:Rarity:Rare SVar:Picture:http://www.wizards.com/global/images/magic/general/psionic_entity.jpg End diff --git a/src/forge/AbilityFactory.java b/src/forge/AbilityFactory.java index 9600940f4fc..fc0afb79068 100644 --- a/src/forge/AbilityFactory.java +++ b/src/forge/AbilityFactory.java @@ -20,6 +20,7 @@ public class AbilityFactory { private boolean isAb = false; private boolean isSp = false; + private boolean isDb = false; public boolean isAbility() { @@ -31,6 +32,10 @@ public class AbilityFactory { return isSp; } + public boolean isDrawback() { + return isDb; + } + private Ability_Cost abCost = null; public Ability_Cost getAbCost() @@ -120,14 +125,18 @@ public class AbilityFactory { isSp = true; API = mapParams.get("SP"); } + else if (mapParams.containsKey("DB")) { + isDb = true; + API = mapParams.get("DB"); + } else throw new RuntimeException("AbilityFactory : getAbility -- no API in " + hostCard.getName()); - - if (!mapParams.containsKey("Cost")) - throw new RuntimeException("AbilityFactory : getAbility -- no Cost in " + hostCard.getName()); - abCost = new Ability_Cost(mapParams.get("Cost"), hostCard.getName(), isAb); - + if (!isDb){ + if (!mapParams.containsKey("Cost")) + throw new RuntimeException("AbilityFactory : getAbility -- no Cost in " + hostCard.getName()); + abCost = new Ability_Cost(mapParams.get("Cost"), hostCard.getName(), isAb); + } if (mapParams.containsKey("ValidTgts")) { @@ -145,8 +154,11 @@ public class AbilityFactory { int min = mapParams.containsKey("TargetMin") ? Integer.parseInt(mapParams.get("TargetMin")) : 1; int max = mapParams.containsKey("TargetMax") ? Integer.parseInt(mapParams.get("TargetMax")) : 1; - if (hasValid) - abTgt = new Target(mapParams.get("TgtPrompt"), mapParams.get("ValidTgts").split(","), min, max); + if (hasValid){ + // TgtPrompt now optional + String prompt = mapParams.containsKey("TgtPrompt") ? mapParams.get("TgtPrompt") : "Select target " + mapParams.get("ValidTgts"); + abTgt = new Target(prompt, mapParams.get("ValidTgts").split(","), min, max); + } else abTgt = new Target(mapParams.get("Tgt"), min, max); @@ -161,29 +173,18 @@ public class AbilityFactory { // *********************************** // Match API keywords - if (API.equals("DealDamage")) - { - final int NumDmg[] = {-1}; - final String NumDmgX[] = {"none"}; - String tmpND = mapParams.get("NumDmg"); - if (tmpND.length() > 0) - { - if (tmpND.matches("X")) - NumDmgX[0] = hostCard.getSVar(tmpND); - - else if (tmpND.matches("[0-9][0-9]?")) - NumDmg[0] = Integer.parseInt(tmpND); - } + if (API.equals("DealDamage")) + { + AbilityFactory_DealDamage dd = new AbilityFactory_DealDamage(this); - AbilityFactory_DealDamage dd = new AbilityFactory_DealDamage(); - - if (isAb) - SA = dd.getAbility(this, NumDmg[0], NumDmgX[0]); + if (isAb) + SA = dd.getAbility(); else if (isSp) - SA = dd.getSpell(this, NumDmg[0], NumDmgX[0]); - - - } + SA = dd.getSpell(); + else if (isDb) + SA = dd.getDrawback(); + SA.setSubAbility(dd.getSubAbility()); + } if (API.equals("PutCounter")){ if (isAb) @@ -240,9 +241,12 @@ public class AbilityFactory { if (API.equals("GainLife")){ if (isAb) SA = AbilityFactory_AlterLife.createAbilityGainLife(this); - if (isSp){ + else if (isSp) SA = AbilityFactory_AlterLife.createSpellGainLife(this); - } + else if (isDb) + SA = AbilityFactory_AlterLife.createSpellGainLife(this); + if(hasSubAbility()) + SA.setSubAbility(getSubAbility()); } if (API.equals("LoseLife")){ @@ -297,17 +301,23 @@ public class AbilityFactory { if (API.equals("Draw")){ if (isAb) SA = AbilityFactory_ZoneAffecting.createAbilityDraw(this); - if (isSp){ + else if (isSp) SA = AbilityFactory_ZoneAffecting.createSpellDraw(this); - } + else if (isDb) + SA = AbilityFactory_ZoneAffecting.createDrawbackDraw(this); + if(hasSubAbility()) + SA.setSubAbility(getSubAbility()); } if (API.equals("Mill")){ if (isAb) SA = AbilityFactory_ZoneAffecting.createAbilityMill(this); - if (isSp){ + else if (isSp) SA = AbilityFactory_ZoneAffecting.createSpellMill(this); - } + else if (isDb) + SA = AbilityFactory_ZoneAffecting.createDrawbackMill(this); + if(hasSubAbility()) + SA.setSubAbility(getSubAbility()); } if (API.equals("Destroy")){ @@ -360,7 +370,7 @@ public class AbilityFactory { SA.setDescription(sb.toString()); } - + if (!isTargeted) SA.setStackDescription(hostCard.getName()); @@ -402,6 +412,27 @@ public class AbilityFactory { return SA; } + // Easy creation of SubAbilities + public Ability_Sub getSubAbility(){ + Ability_Sub abSub = null; + + String sSub = getMapParams().get("SubAbility"); + + if (sSub.startsWith("SVar=")) + sSub = getHostCard().getSVar(sSub.split("=")[1]); + + if (sSub.startsWith("DB$")) + { + AbilityFactory afDB = new AbilityFactory(); + abSub = (Ability_Sub)afDB.getAbility(sSub, getHostCard()); + } + else{ + // Older style Drawback. May not be necessary? + } + + return abSub; + } + } diff --git a/src/forge/AbilityFactory_AlterLife.java b/src/forge/AbilityFactory_AlterLife.java index 9722c195c88..1684eee8035 100644 --- a/src/forge/AbilityFactory_AlterLife.java +++ b/src/forge/AbilityFactory_AlterLife.java @@ -18,15 +18,7 @@ public class AbilityFactory_AlterLife { @Override public String getStackDescription(){ // when getStackDesc is called, just build exactly what is happening - StringBuilder sb = new StringBuilder(); - String name = af.getHostCard().getName(); - int amount = calculateAmount(af.getHostCard(), params.get("LifeAmount"), this); - String player = "You gain "; - if (af.getAbTgt() != null) - player = getTargetPlayer() + " gains "; - sb.append(name).append(" - ").append(player).append(amount).append(" life."); - - return sb.toString(); + return gainLifeStackDescription(af, this); } public boolean canPlay(){ @@ -59,15 +51,7 @@ public class AbilityFactory_AlterLife { @Override public String getStackDescription(){ // when getStackDesc is called, just build exactly what is happening - StringBuilder sb = new StringBuilder(); - String name = af.getHostCard().getName(); - int amount = calculateAmount(af.getHostCard(), params.get("LifeAmount"), this); - String player = "You gain "; - if (af.getAbTgt() != null) - player = getTargetPlayer() + " gains "; - sb.append(name).append(" - ").append(player).append(amount).append(" life."); - - return sb.toString(); + return gainLifeStackDescription(af, this); } public boolean canPlay(){ @@ -93,6 +77,42 @@ public class AbilityFactory_AlterLife { return spGainLife; } + public static SpellAbility createDrawbackGainLife(final AbilityFactory AF){ + final SpellAbility dbGainLife = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()){ + private static final long serialVersionUID = 6631124959690157874L; + + final AbilityFactory af = AF; + final HashMap params = af.getMapParams(); + + @Override + public String getStackDescription(){ + // when getStackDesc is called, just build exactly what is happening + return gainLifeStackDescription(af, this); + } + + public boolean canPlayAI() + { + // if X depends on abCost, the AI needs to choose which card he would sacrifice first + // then call xCount with that card to properly calculate the amount + // Or choosing how many to sacrifice + return gainLifeCanPlayAI(af, this, params.get("LifeAmount")); + } + + @Override + public void resolve() { + int amount = calculateAmount(af.getHostCard(), params.get("LifeAmount"), this); + gainLifeResolve(af, this, amount); + } + + @Override + public boolean chkAI_Drawback() { + return true; + } + + }; + return dbGainLife; + } + public static SpellAbility createAbilityLoseLife(final AbilityFactory AF){ final SpellAbility abLoseLife = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()){ private static final long serialVersionUID = 1129762905315395160L; @@ -181,14 +201,7 @@ public class AbilityFactory_AlterLife { }; return abLoseLife; } - - public static SpellAbility createAbilitySyphonLife(final AbilityFactory AF){ // tgt loses X life, you gain X life - return null; - } - - public static SpellAbility createSpellSyphonLife(final AbilityFactory AF){ // tgt loses X life, you gain X life - return null; - } + public static int calculateAmount(Card card, String lifeAmount, SpellAbility ability){ if (lifeAmount.matches("X")) @@ -212,6 +225,29 @@ public class AbilityFactory_AlterLife { return Integer.parseInt(lifeAmount); } + public static String gainLifeStackDescription(AbilityFactory af, SpellAbility sa){ + StringBuilder sb = new StringBuilder(); + int amount = calculateAmount(af.getHostCard(), af.getMapParams().get("LifeAmount"), sa); + + if (!(sa instanceof Ability_Sub)) + sb.append(sa.getSourceCard().getName()).append(" - "); + else + sb.append(" "); + + String player = "You gain "; + if (af.getAbTgt() != null) + player = sa.getTargetPlayer() + " gains "; + sb.append(player).append(amount).append(" life."); + + Ability_Sub abSub = sa.getSubAbility(); + if (abSub != null) { + abSub.setParent(sa); + sb.append(abSub.getStackDescription()); + } + + return sb.toString(); + } + public static boolean gainLifeCanPlayAI(final AbilityFactory af, final SpellAbility sa, final String amountStr){ Random r = new Random(); Ability_Cost abCost = sa.getPayCosts(); @@ -260,7 +296,6 @@ public class AbilityFactory_AlterLife { public static void gainLifeResolve(final AbilityFactory af, final SpellAbility sa, int lifeAmount){ HashMap params = af.getMapParams(); - String DrawBack = params.get("SubAbility"); Card card = af.getHostCard(); ArrayList tgtPlayers; @@ -277,8 +312,20 @@ public class AbilityFactory_AlterLife { if (tgt == null || p.canTarget(af.getHostCard())) p.gainLife(lifeAmount, sa.getSourceCard()); - if (af.hasSubAbility()) - CardFactoryUtil.doDrawBack(DrawBack, lifeAmount, card.getController(), card.getController().getOpponent(), tgtPlayers.get(0), card, null, sa); + + if (af.hasSubAbility()){ + Ability_Sub abSub = sa.getSubAbility(); + if (abSub != null){ + if (abSub.getParent() == null) + abSub.setParent(sa); + abSub.resolve(); + } + else{ + String DrawBack = params.get("SubAbility"); + if (af.hasSubAbility()) + CardFactoryUtil.doDrawBack(DrawBack, lifeAmount, card.getController(), card.getController().getOpponent(), tgtPlayers.get(0), card, null, sa); + } + } } public static boolean loseLifeCanPlayAI(final AbilityFactory af, final SpellAbility sa, final String amountStr){ diff --git a/src/forge/AbilityFactory_DealDamage.java b/src/forge/AbilityFactory_DealDamage.java index 32142b992d8..4ae6bb2935c 100644 --- a/src/forge/AbilityFactory_DealDamage.java +++ b/src/forge/AbilityFactory_DealDamage.java @@ -1,316 +1,397 @@ -package forge; -import java.util.ArrayList; -import java.util.Random; + package forge; -public class AbilityFactory_DealDamage { + import java.util.ArrayList; + import java.util.Random; - private AbilityFactory AF = null; - - private int nDamage = -1; - - private String XDamage = "none"; - - private boolean TgtOpp = false; - - public SpellAbility getAbility(final AbilityFactory af, final int NumDmg, final String NumDmgX) - { - AF = af; - nDamage = NumDmg; - XDamage = NumDmgX; - - if(af.getMapParams().containsKey("Tgt")) - if (AF.getMapParams().get("Tgt").equals("TgtOpp")) - TgtOpp = true; - - final SpellAbility abDamage = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) - { - private static final long serialVersionUID = -7560349014757367722L; - - @Override - public boolean canPlay(){ - return super.canPlay(); + public class AbilityFactory_DealDamage { + private AbilityFactory AF = null; + + private int nDamage = -1; + + private String XDamage = "none"; + + private boolean TgtOpp = false; + + private Ability_Sub subAbAF = null; + private boolean hasSubAbAF = false; + private String subAbStr = "none"; + private boolean hasSubAbStr = false; + + public Ability_Sub getSubAbility() { return subAbAF; } + + public AbilityFactory_DealDamage(AbilityFactory newAF) + { + AF = newAF; + + String tmpND = AF.getMapParams().get("NumDmg"); + if (tmpND.length() > 0) + { + if (tmpND.matches("[xX]")) + XDamage = AF.getHostCard().getSVar(tmpND.substring(1)); + + else if (tmpND.matches("[0-9][0-9]?")) + nDamage = Integer.parseInt(tmpND); } - - @Override - public boolean canPlayAI() { - return doCanPlayAI(this); - - } - - @Override - public String getStackDescription(){ - return damageStackDescription(AF, this); - } - - @Override - public void resolve() { - doResolve(this); - AF.getHostCard().setAbilityUsed(AF.getHostCard().getAbilityUsed() + 1); - - } - };//Ability_Activated - - return abDamage; - } - - public SpellAbility getSpell(final AbilityFactory af, final int NumDmg, final String NumDmgX) - { - AF = af; - nDamage = NumDmg; - XDamage = NumDmgX; - - if(af.getMapParams().containsKey("Tgt")) - if (AF.getMapParams().get("Tgt").equals("TgtOpp")) - TgtOpp = true; - - final SpellAbility spDealDamage = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { - private static final long serialVersionUID = 7239608350643325111L; - - @Override - public boolean canPlay(){ - return super.canPlay(); - } - - @Override - public boolean canPlayAI() { - return doCanPlayAI(this); - - } - - @Override - public String getStackDescription(){ - return damageStackDescription(AF, this); - } - - @Override - public void resolve() { - doResolve(this); - + + if(AF.getMapParams().containsKey("Tgt")) + if (AF.getMapParams().get("Tgt").equals("TgtOpp")) + TgtOpp = true; + + if(AF.hasSubAbility()) + { + String sSub = AF.getMapParams().get("SubAbility"); + + if (sSub.startsWith("SVar=")) + sSub = AF.getHostCard().getSVar(sSub.split("=")[1]); + + if (sSub.startsWith("DB$")) + { + AbilityFactory afDB = new AbilityFactory(); + subAbAF = (Ability_Sub)afDB.getAbility(sSub, AF.getHostCard()); + hasSubAbAF = true; + } + else + { + subAbStr = sSub; + hasSubAbStr = true; + } } - - }; // Spell - - return spDealDamage; - } - - private int getNumDamage(SpellAbility saMe) { - if(nDamage != -1) return nDamage; - - String calcX[] = XDamage.split("\\$"); - - if (calcX.length == 1 || calcX[1].equals("none")) - return 0; - - if (calcX[0].startsWith("Count")) - { - return CardFactoryUtil.xCount(AF.getHostCard(), calcX[1]); - } - else if (calcX[0].startsWith("Sacrificed")) - { - return CardFactoryUtil.handlePaid(saMe.getSacrificedCost(), calcX[1]); - } - - return 0; - } - - private boolean shouldTgtP(int d) { - PlayerZone compHand = AllZone.getZone(Constant.Zone.Hand, AllZone.ComputerPlayer); - CardList hand = new CardList(compHand.getCards()); - - if(AF.isSpell() && hand.size() > 7) // anti-discard-at-EOT - return true; - - if(AllZone.HumanPlayer.getLife() - d < 10) // if damage from this spell would drop the human to less than 10 life - return true; - - return false; - } - - private Card chooseTgtC(final int d) { - // Combo alert!! - PlayerZone compy = AllZone.getZone(Constant.Zone.Play, AllZone.ComputerPlayer); - CardList cPlay = new CardList(compy.getCards()); - if(cPlay.size() > 0) for(int i = 0; i < cPlay.size(); i++) - if(cPlay.get(i).getName().equals("Stuffy Doll")) return cPlay.get(i); - - PlayerZone human = AllZone.getZone(Constant.Zone.Play, AllZone.HumanPlayer); - CardList hPlay = new CardList(human.getCards()); - hPlay = hPlay.filter(new CardListFilter() { - public boolean addCard(Card c) { - // will include creatures already dealt damage - return c.isCreature() && ((c.getNetDefense() + c.getDamage()) <= d) - && CardFactoryUtil.canTarget(AF.getHostCard(), c); - } - }); - - if(hPlay.size() > 0) { - Card best = hPlay.get(0); - - if(hPlay.size() > 1) { - for(int i = 1; i < hPlay.size(); i++) { - Card b = hPlay.get(i); - // choose best overall creature? - if(b.getSpellAbility().length > best.getSpellAbility().length - || b.getKeyword().size() > best.getKeyword().size() - || b.getNetAttack() > best.getNetAttack()) best = b; + } + + public SpellAbility getAbility() + { + final SpellAbility abDamage = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) + { + private static final long serialVersionUID = -7560349014757367722L; + + @Override + public boolean canPlay(){ + return super.canPlay(); } + + @Override + public boolean canPlayAI() { + return doCanPlayAI(this); + + } + + @Override + public String getStackDescription(){ + return damageStackDescription(AF, this); + } + + @Override + public void resolve() { + doResolve(this); + AF.getHostCard().setAbilityUsed(AF.getHostCard().getAbilityUsed() + 1); + + } + };//Ability_Activated + + return abDamage; + } + + public SpellAbility getSpell() + { + final SpellAbility spDealDamage = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { + private static final long serialVersionUID = 7239608350643325111L; + + @Override + public boolean canPlay(){ + return super.canPlay(); + } + + @Override + public boolean canPlayAI() { + return doCanPlayAI(this); + + } + + @Override + public String getStackDescription(){ + return damageStackDescription(AF, this); + } + + @Override + public void resolve() { + doResolve(this); + + } + + + }; // Spell + + return spDealDamage; + } + + public SpellAbility getDrawback() + { + final SpellAbility dbDealDamage = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) { + private static final long serialVersionUID = 7239608350643325111L; + + @Override + public boolean chkAI_Drawback() { + return doCanPlayAI(this); + } + + @Override + public String getStackDescription() { + return damageStackDescription(AF, this); + } + + @Override + public void resolve() { + doResolve(this); + } + + }; // Spell + + return dbDealDamage; + } + + + private int getNumDamage(SpellAbility saMe) { + if(nDamage != -1) return nDamage; + + String calcX[] = XDamage.split("\\$"); + + if (calcX.length == 1 || calcX[1].equals("none")) + return 0; + + if (calcX[0].startsWith("Count")) + { + return CardFactoryUtil.xCount(AF.getHostCard(), calcX[1]); + } + else if (calcX[0].startsWith("Sacrificed")) + { + return CardFactoryUtil.handlePaid(saMe.getSacrificedCost(), calcX[1]); + } + + return 0; + } + + private boolean shouldTgtP(int d) { + PlayerZone compHand = AllZone.getZone(Constant.Zone.Hand, AllZone.ComputerPlayer); + CardList hand = new CardList(compHand.getCards()); + + if(AF.isSpell() && hand.size() > 7) // anti-discard-at-EOT + return true; + + if(AllZone.HumanPlayer.getLife() - d < 10) // if damage from this spell would drop the human to less than 10 life + return true; + + return false; + } + + private Card chooseTgtC(final int d) { + // Combo alert!! + PlayerZone compy = AllZone.getZone(Constant.Zone.Play, AllZone.ComputerPlayer); + CardList cPlay = new CardList(compy.getCards()); + if(cPlay.size() > 0) for(int i = 0; i < cPlay.size(); i++) + if(cPlay.get(i).getName().equals("Stuffy Doll")) return cPlay.get(i); + + PlayerZone human = AllZone.getZone(Constant.Zone.Play, AllZone.HumanPlayer); + CardList hPlay = new CardList(human.getCards()); + hPlay = hPlay.filter(new CardListFilter() { + public boolean addCard(Card c) { + // will include creatures already dealt damage + return c.isCreature() && ((c.getNetDefense() + c.getDamage()) <= d) + && CardFactoryUtil.canTarget(AF.getHostCard(), c); + } + }); + + if(hPlay.size() > 0) { + Card best = hPlay.get(0); + + if(hPlay.size() > 1) { + for(int i = 1; i < hPlay.size(); i++) { + Card b = hPlay.get(i); + // choose best overall creature? + if(b.getSpellAbility().length > best.getSpellAbility().length + || b.getKeyword().size() > best.getKeyword().size() + || b.getNetAttack() > best.getNetAttack()) best = b; + } + } + return best; } - return best; + return null; } - return null; - } - private boolean doCanPlayAI(SpellAbility saMe) - { - // temporarily disabled until better AI - if (AF.getAbCost().getSacCost()) return false; - if (AF.getAbCost().getSubCounter()) return false; - if (AF.getAbCost().getLifeCost()) return false; - - if (!ComputerUtil.canPayCost(saMe)) - return false; - - // TODO handle proper calculation of X values based on Cost - int damage = getNumDamage(saMe); - - boolean rr = AF.isSpell(); - - if (AF.isAbility()) + private boolean doCanPlayAI(SpellAbility saMe) { - Random r = new Random(); // prevent run-away activations - if(r.nextFloat() <= Math.pow(.6667, AF.getHostCard().getAbilityUsed())) - rr = true; + // temporarily disabled until better AI + if (AF.getAbCost().getSacCost()) return false; + if (AF.getAbCost().getSubCounter()) return false; + if (AF.getAbCost().getLifeCost()) return false; + + if (!ComputerUtil.canPayCost(saMe)) + return false; + + // TODO handle proper calculation of X values based on Cost + int damage = getNumDamage(saMe); + + boolean rr = AF.isSpell(); + + if (AF.isAbility()) + { + Random r = new Random(); // prevent run-away activations + if(r.nextFloat() <= Math.pow(.6667, AF.getHostCard().getAbilityUsed())) + rr = true; + } + + Target tgt = AF.getAbTgt(); + // AI handle multi-targeting? + tgt.resetTargets(); + // target loop + while(tgt.getNumTargeted() < tgt.getMaxTargets()){ + // TODO: Consider targeting the planeswalker + if(tgt.canTgtCreatureAndPlayer()) { + + if(shouldTgtP(damage)) { + tgt.addTarget(AllZone.HumanPlayer); + continue; + } + + Card c = chooseTgtC(damage); + if(c != null) { + tgt.addTarget(c); + continue; + } + } + + if(tgt.canTgtPlayer() || TgtOpp) { + tgt.addTarget(AllZone.HumanPlayer); + continue; + } + + if(tgt.canTgtCreature()) { + Card c = chooseTgtC(damage); + if(c != null) { + tgt.addTarget(c); + continue; + } + } + // fell through all the choices, no targets left? + if (tgt.getNumTargeted() < tgt.getMinTargets() || tgt.getNumTargeted() == 0){ + tgt.resetTargets(); + return false; + } + else{ + // todo is this good enough? for up to amounts? + break; + } + } + + return rr; + + } + + private String damageStackDescription(AbilityFactory af, SpellAbility sa){ + // when damageStackDescription is called, just build exactly what is happening + StringBuilder sb = new StringBuilder(); + String name = af.getHostCard().getName(); + int damage = getNumDamage(sa); + + ArrayList tgts = findTargets(sa); + + if (!(sa instanceof Ability_Sub)) + sb.append(name).append(" - "); + + sb.append("Deals ").append(damage).append(" damage to "); + for(int i = 0; i < tgts.size(); i++){ + if (i != 0) + sb.append(" "); + + Object o = tgts.get(0); + if (o instanceof Player){ + sb.append(((Player)o).getName()); + } + else{ + sb.append(((Card)o).getName()); + } + + } + sb.append(". "); + + if (hasSubAbAF){ + subAbAF.setParent(sa); + sb.append(subAbAF.getStackDescription()); + } + + return sb.toString(); + } + + private ArrayList findTargets(SpellAbility saMe){ + Target tgt = AF.getAbTgt(); + ArrayList tgts; + if (tgt != null) + tgts = tgt.getTargets(); + else{ + tgts = new ArrayList(); + if (TgtOpp){ + tgts.add(saMe.getActivatingPlayer().getOpponent()); + } + else if (AF.getMapParams().containsKey("Affected")){ + String affected = AF.getMapParams().get("Affected"); + if (affected.equals("You")) + tgts.add(saMe.getActivatingPlayer()); + else if (affected.equals("Self")) + tgts.add(saMe.getSourceCard()); + } + } + return tgts; } - Target tgt = AF.getAbTgt(); - // AI handle multi-targeting? - tgt.resetTargets(); - // target loop - while(tgt.getNumTargeted() < tgt.getMaxTargets()){ - // TODO: Consider targeting the planeswalker - if(tgt.canTgtCreatureAndPlayer()) { + private void doResolve(SpellAbility saMe) + { + int damage = getNumDamage(saMe); - if(shouldTgtP(damage)) { - tgt.addTarget(AllZone.HumanPlayer); - continue; - } - - Card c = chooseTgtC(damage); - if(c != null) { - tgt.addTarget(c); - continue; - } - } - - if(tgt.canTgtPlayer() || TgtOpp) { - tgt.addTarget(AllZone.HumanPlayer); - continue; - } - - if(tgt.canTgtCreature()) { - Card c = chooseTgtC(damage); - if(c != null) { - tgt.addTarget(c); - continue; - } - } - // fell through all the choices, no targets left? - if (tgt.getNumTargeted() < tgt.getMinTargets() || tgt.getNumTargeted() == 0){ - tgt.resetTargets(); - return false; - } - else{ - // todo is this good enough? for up to amounts? - break; - } - } - - return rr; - + ArrayList tgts = findTargets(saMe); + boolean targeted = (AF.getAbTgt() != null) || TgtOpp; + + if (tgts == null || tgts.size() == 0){ + System.out.println("No targets?"); + return; + } + + for(Object o : tgts){ + if (o instanceof Card){ + Card c = (Card)o; + if(AllZone.GameAction.isCardInPlay(c) && (!targeted || CardFactoryUtil.canTarget(AF.getHostCard(), c))) + c.addDamage(damage, AF.getHostCard()); + } + else if (o instanceof Player){ + Player p = (Player) o; + if (!targeted || p.canTarget(AF.getHostCard())) + p.addDamage(damage, AF.getHostCard()); + } + } + + if (hasSubAbAF) { + if (subAbAF.getParent() == null) + subAbAF.setParent(saMe); + subAbAF.resolve(); + } + + else if (hasSubAbStr){ + Object obj = tgts.get(0); + + Player pl = null; + Card c = null; + + if (obj instanceof Card){ + c = (Card)obj; + pl = c.getController(); + } + else{ + pl = (Player)obj; + } + CardFactoryUtil.doDrawBack(subAbStr, damage, AF.getHostCard().getController(), + AF.getHostCard().getController().getOpponent(), pl, AF.getHostCard(), c, saMe); + + } + + } } - - private String damageStackDescription(AbilityFactory af, SpellAbility sa){ - // when damageStackDescription is called, just build exactly what is happening - StringBuilder sb = new StringBuilder(); - String name = af.getHostCard().getName(); - int damage = getNumDamage(sa); - - ArrayList tgts; - Target tgt = AF.getAbTgt(); - if (tgt != null) - tgts = tgt.getTargets(); - else{ - tgts = new ArrayList(); - if (TgtOpp) - tgts.add(AF.getHostCard().getController().getOpponent()); - } - - sb.append(name).append(" - "); - sb.append("Deals ").append(damage).append(" damage to "); - for(int i = 0; i < tgts.size(); i++){ - Object o = tgts.get(0); - if (o instanceof Player){ - sb.append(((Player)o).getName()); - } - else{ - sb.append(((Card)o).getName()); - } - sb.append(" "); - } - - return sb.toString(); - } - - private void doResolve(SpellAbility saMe) - { - int damage = getNumDamage(saMe); - - ArrayList tgts; - Target tgt = AF.getAbTgt(); - if (tgt != null) - tgts = tgt.getTargets(); - else{ - tgts = new ArrayList(); - if (TgtOpp) - tgts.add(AF.getHostCard().getController().getOpponent()); - } - - if (tgts.size() == 0){ - System.out.println("No targets?"); - return; - } - - for(Object o : tgts){ - if (o instanceof Card){ - Card c = (Card)o; - if(AllZone.GameAction.isCardInPlay(c) && CardFactoryUtil.canTarget(AF.getHostCard(), c)) - c.addDamage(damage, AF.getHostCard()); - } - else if (o instanceof Player){ - Player p = (Player) o; - if (p.canTarget(AF.getHostCard())) - p.addDamage(damage, AF.getHostCard()); - } - } - - Object obj = tgts.get(0); - - Player pl = null; - Card c = null; - - if (obj instanceof Card){ - c = (Card)obj; - pl = c.getController(); - } - else{ - pl = (Player)obj; - } - - if(AF.hasSubAbility()) - CardFactoryUtil.doDrawBack(AF.getMapParams().get("SubAbility"), damage, - AF.getHostCard().getController(), AF.getHostCard().getController().getOpponent(), - pl, AF.getHostCard(), c, saMe); - } -} diff --git a/src/forge/AbilityFactory_ZoneAffecting.java b/src/forge/AbilityFactory_ZoneAffecting.java index f8fb2d10f83..db1dda8433c 100644 --- a/src/forge/AbilityFactory_ZoneAffecting.java +++ b/src/forge/AbilityFactory_ZoneAffecting.java @@ -14,17 +14,7 @@ public class AbilityFactory_ZoneAffecting { @Override public String getStackDescription(){ // when getStackDesc is called, just build exactly what is happening - Player player = af.getAbTgt() == null ? getActivatingPlayer() : getTargetPlayer(); - StringBuilder sb = new StringBuilder(); - - sb.append(getSourceCard().getName()); - sb.append(" - "); - sb.append(player.toString()); - sb.append(" draws ("); - sb.append(af.getMapParams().get("NumCards")); - sb.append(")"); - - return sb.toString(); + return drawStackDescription(af, this); } public boolean canPlay(){ @@ -55,17 +45,7 @@ public class AbilityFactory_ZoneAffecting { @Override public String getStackDescription(){ // when getStackDesc is called, just build exactly what is happening - Player player = af.getAbTgt() == null ? getActivatingPlayer() : getTargetPlayer(); - StringBuilder sb = new StringBuilder(); - - sb.append(getSourceCard().getName()); - sb.append(" - "); - sb.append(player.toString()); - sb.append(" draws ("); - sb.append(af.getMapParams().get("NumCards")); - sb.append(")"); - - return sb.toString(); + return drawStackDescription(af, this); } public boolean canPlay(){ @@ -87,6 +67,56 @@ public class AbilityFactory_ZoneAffecting { return spDraw; } + public static SpellAbility createDrawbackDraw(final AbilityFactory AF){ + final SpellAbility dbDraw = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()){ + private static final long serialVersionUID = -4990932993654533449L; + + final AbilityFactory af = AF; + + @Override + public String getStackDescription(){ + // when getStackDesc is called, just build exactly what is happening + return drawStackDescription(af, this); + } + + @Override + public void resolve() { + drawResolve(af, this); + } + + @Override + public boolean chkAI_Drawback() { + // TODO Auto-generated method stub + return true; + } + + }; + return dbDraw; + } + + public static String drawStackDescription(AbilityFactory af, SpellAbility sa){ + Player player = af.getAbTgt() == null ? sa.getActivatingPlayer() : sa.getTargetPlayer(); + StringBuilder sb = new StringBuilder(); + + if (!(sa instanceof Ability_Sub)) + sb.append(sa.getSourceCard().getName()).append(" - "); + else + sb.append(" "); + + sb.append(player.toString()); + sb.append(" draws ("); + sb.append(af.getMapParams().get("NumCards")); + sb.append(")."); + + Ability_Sub abSub = sa.getSubAbility(); + if (abSub != null){ + abSub.setParent(sa); + sb.append(abSub.getStackDescription()); + } + + return sb.toString(); + } + public static boolean drawCanPlayAI(final AbilityFactory af, SpellAbility sa){ // AI cannot use this properly until he can use SAs during Humans turn if (!ComputerUtil.canPayCost(sa)) @@ -161,13 +191,28 @@ public class AbilityFactory_ZoneAffecting { } for(Player p : tgtPlayers) - if (tgt == null || p.canTarget(af.getHostCard())) - p.drawCards(numCards); - - String DrawBack = params.get("SubAbility"); - if (af.hasSubAbility()) - CardFactoryUtil.doDrawBack(DrawBack, 0, source.getController(), source.getController().getOpponent(), source.getController(), source, null, sa); + if (tgt == null || p.canTarget(af.getHostCard())){ + if (params.containsKey("NextUpkeep")) + for(int i = 0; i < numCards; i++) + p.addSlowtripList(source); + else + p.drawCards(numCards); + + } + if (af.hasSubAbility()){ + Ability_Sub abSub = sa.getSubAbility(); + if (abSub != null){ + if (abSub.getParent() == null) + abSub.setParent(sa); + abSub.resolve(); + } + else{ + String DrawBack = params.get("SubAbility"); + if (af.hasSubAbility()) + CardFactoryUtil.doDrawBack(DrawBack, 0, source.getController(), source.getController().getOpponent(), source.getController(), source, null, sa); + } + } } @@ -181,18 +226,7 @@ public class AbilityFactory_ZoneAffecting { @Override public String getStackDescription(){ - // when getStackDesc is called, just build exactly what is happening - Player player = af.getAbTgt() == null ? getActivatingPlayer() : getTargetPlayer(); - StringBuilder sb = new StringBuilder(); - - sb.append(getSourceCard().getName()); - sb.append(" - Mills "); - sb.append(af.getMapParams().get("NumCards")); - sb.append(" Cards from "); - sb.append(player.toString()); - sb.append("'s library."); - - return sb.toString(); + return millStackDescription(this, af); } public boolean canPlay(){ @@ -220,6 +254,11 @@ public class AbilityFactory_ZoneAffecting { final AbilityFactory af = AF; + @Override + public String getStackDescription(){ + return millStackDescription(this, af); + } + public boolean canPlay(){ // super takes care of AdditionalCosts return super.canPlay(); @@ -239,6 +278,61 @@ public class AbilityFactory_ZoneAffecting { return spMill; } + public static SpellAbility createDrawbackMill(final AbilityFactory AF){ + final SpellAbility dbMill = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()){ + private static final long serialVersionUID = -4990932993654533449L; + + final AbilityFactory af = AF; + + @Override + public String getStackDescription(){ + return millStackDescription(this, af); + } + + public boolean canPlayAI() + { + return millCanPlayAI(af, this); + } + + @Override + public void resolve() { + millResolve(af, this); + } + + @Override + public boolean chkAI_Drawback() { + return true; + } + + }; + return dbMill; + } + + public static String millStackDescription(SpellAbility sa, AbilityFactory af){ + // when getStackDesc is called, just build exactly what is happening + Player player = af.getAbTgt() == null ? sa.getActivatingPlayer() : sa.getTargetPlayer(); + StringBuilder sb = new StringBuilder(); + + if (!(sa instanceof Ability_Sub)) + sb.append(sa.getSourceCard().getName()).append(" - "); + else + sb.append(" "); + + sb.append("Mills "); + sb.append(af.getMapParams().get("NumCards")); + sb.append(" Card(s) from "); + sb.append(player.toString()); + sb.append("'s library."); + + Ability_Sub abSub = sa.getSubAbility(); + if (abSub != null){ + abSub.setParent(sa); + sb.append(abSub.getStackDescription()); + } + + return sb.toString(); + } + public static boolean millCanPlayAI(final AbilityFactory af, SpellAbility sa){ // AI cannot use this properly until he can use SAs during Humans turn if (!ComputerUtil.canPayCost(sa)) @@ -304,6 +398,7 @@ public class AbilityFactory_ZoneAffecting { HashMap params = af.getMapParams(); Card source = sa.getSourceCard(); + // todo: handle deciding what X would be around here for Psychic Drain type cards int numCards = Integer.parseInt(params.get("NumCards")); ArrayList tgtPlayers; @@ -320,9 +415,18 @@ public class AbilityFactory_ZoneAffecting { if (tgt == null || p.canTarget(af.getHostCard())) p.mill(numCards); - String DrawBack = params.get("SubAbility"); - if (af.hasSubAbility()) - CardFactoryUtil.doDrawBack(DrawBack, 0, source.getController(), source.getController().getOpponent(), tgtPlayers.get(0), source, null, sa); - + if (af.hasSubAbility()){ + Ability_Sub abSub = sa.getSubAbility(); + if (abSub != null){ + if (abSub.getParent() == null) + abSub.setParent(sa); + abSub.resolve(); + } + else{ + String DrawBack = params.get("SubAbility"); + if (af.hasSubAbility()) + CardFactoryUtil.doDrawBack(DrawBack, 0, source.getController(), source.getController().getOpponent(), tgtPlayers.get(0), source, null, sa); + } + } } } diff --git a/src/forge/Ability_Sub.java b/src/forge/Ability_Sub.java new file mode 100644 index 00000000000..e0c352b8532 --- /dev/null +++ b/src/forge/Ability_Sub.java @@ -0,0 +1,29 @@ +package forge; + +abstract public class Ability_Sub extends SpellAbility implements java.io.Serializable { + private static final long serialVersionUID = 4650634415821733134L; + + private SpellAbility parent = null; + + public Ability_Sub(Card sourceCard, Target tgt) { + super(SpellAbility.Ability, sourceCard); + setTarget(tgt); + } + + @Override + public boolean canPlay() { + // this should never be on the Stack by itself + return false; + } + + abstract public boolean chkAI_Drawback(); + + public void setParent(SpellAbility parent) { + this.parent = parent; + this.setActivatingPlayer(parent.getActivatingPlayer()); + } + + public SpellAbility getParent() { + return parent; + } +} diff --git a/src/forge/MagicStack.java b/src/forge/MagicStack.java index dff0735923d..1373c278821 100644 --- a/src/forge/MagicStack.java +++ b/src/forge/MagicStack.java @@ -440,32 +440,7 @@ public class MagicStack extends MyObservable { AllZone.Phase.resetPriority(); // ActivePlayer gains priority first after Resolve Card source = sa.getSourceCard(); - boolean fizzle = false; - - Target tgt = sa.getTarget(); - if (tgt != null){ - fizzle = true; - // With multi-targets, as long as one target is still legal, we'll try to go through as much as possible - ArrayList tgts = tgt.getTargets(); - for(Object o : tgts){ - if (o instanceof Player){ - Player p = (Player)o; - fizzle &= !(p.canTarget(sa.getTargetCard())); - } - if (o instanceof Card){ - Card card = (Card)o; - fizzle &= !(CardFactoryUtil.isTargetStillValid(sa, card)); - } - } - } - else if (sa.getTargetCard() != null) { - // Fizzling will only work for Abilities that use the Target class, - // since the info isn't available otherwise - fizzle = !CardFactoryUtil.isTargetStillValid(sa, sa.getTargetCard()); - } - else if (sa.getTargetPlayer() != null) { - fizzle = !sa.getTargetPlayer().canTarget(source); - } + boolean fizzle = hasFizzled(sa, source); if (!fizzle) { final Card crd = source; @@ -545,6 +520,42 @@ public class MagicStack extends MyObservable { GuiDisplayUtil.updateGUI(); } + + public boolean hasFizzled(SpellAbility sa, Card source){ + boolean fizzle = false; + + Target tgt = sa.getTarget(); + if (tgt != null){ + fizzle = true; + // With multi-targets, as long as one target is still legal, we'll try to go through as much as possible + ArrayList tgts = tgt.getTargets(); + for(Object o : tgts){ + if (o instanceof Player){ + Player p = (Player)o; + fizzle &= !(p.canTarget(sa.getTargetCard())); + } + if (o instanceof Card){ + Card card = (Card)o; + fizzle &= !(CardFactoryUtil.isTargetStillValid(sa, card)); + } + } + } + else if (sa.getTargetCard() != null) { + // Fizzling will only work for Abilities that use the Target class, + // since the info isn't available otherwise + fizzle = !CardFactoryUtil.isTargetStillValid(sa, sa.getTargetCard()); + } + else if (sa.getTargetPlayer() != null) { + fizzle = !sa.getTargetPlayer().canTarget(source); + } + + Ability_Sub abSub = sa.getSubAbility(); + if (abSub != null) + fizzle &= hasFizzled(abSub, source); + + return fizzle; + } + public SpellAbility pop() { SpellAbility sp = (SpellAbility) stack.remove(0); diff --git a/src/forge/SpellAbility.java b/src/forge/SpellAbility.java index 8a26ec03ed1..572216ba054 100644 --- a/src/forge/SpellAbility.java +++ b/src/forge/SpellAbility.java @@ -48,6 +48,7 @@ public abstract class SpellAbility { protected Target chosenTarget = null; private SpellAbility_Restriction restrictions = new SpellAbility_Restriction(); + private Ability_Sub subAbility = null; private CardList sacrificedCards = null; @@ -322,6 +323,14 @@ public abstract class SpellAbility { return description; } + public void setSubAbility(Ability_Sub subAbility) { + this.subAbility = subAbility; + } + + public Ability_Sub getSubAbility() { + return this.subAbility; + } + public Card getTargetCard() { if(targetCard == null){ Target tgt = this.getTarget(); diff --git a/src/forge/SpellAbility_Requirements.java b/src/forge/SpellAbility_Requirements.java index 7b905b3df54..554dcc59d96 100644 --- a/src/forge/SpellAbility_Requirements.java +++ b/src/forge/SpellAbility_Requirements.java @@ -28,13 +28,14 @@ public class SpellAbility_Requirements { // freeze Stack. No abilities should go onto the stack while I'm filling requirements. AllZone.Stack.freezeStack(); - if (select.doesTarget()){ + // Skip to paying if parent ability doesn't target and has no subAbilities. + if (!select.doesTarget() && ability.getSubAbility() == null) + startPaying(); + else{ select.setRequirements(this); select.resetTargets(); select.chooseTargets(); } - else - startPaying(); } public void finishedTargeting(){ diff --git a/src/forge/Target_Selection.java b/src/forge/Target_Selection.java index 57d56844a0d..b7b36b908fc 100644 --- a/src/forge/Target_Selection.java +++ b/src/forge/Target_Selection.java @@ -47,9 +47,23 @@ public class Target_Selection { req.finishedTargeting(); return false; } - else if (bDoneTarget && target.isMinTargetsChosen() || target.isMaxTargetsChosen()){ - req.finishedTargeting(); - return true; + else if (!doesTarget() || bDoneTarget && target.isMinTargetsChosen() || target.isMaxTargetsChosen()){ + 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(); + boolean flag = ts.chooseTargets(); + + return flag; + } } //targets still needed