- 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
This commit is contained in:
jendave
2011-08-06 11:06:06 +00:00
parent b2915e11da
commit 1cdf7b3ef4
13 changed files with 785 additions and 445 deletions

2
.gitattributes vendored
View File

@@ -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/lumbering_satyr.txt -text svneol=native#text/plain
res/cardsfolder/lumengrid_warden.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/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/lure.txt -text svneol=native#text/plain
res/cardsfolder/lurking_informant.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 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_Mana.java -text svneol=native#text/plain
src/forge/Ability_Reflected_Mana.java 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_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_Tap.java svneol=native#text/plain
src/forge/Ability_Triggered.java svneol=native#text/plain src/forge/Ability_Triggered.java svneol=native#text/plain
src/forge/AllZone.java svneol=native#text/plain src/forge/AllZone.java svneol=native#text/plain

View File

@@ -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

View File

@@ -2,8 +2,9 @@ Name:Orcish Cannonade
ManaCost:1 R R ManaCost:1 R R
Types:Instant Types:Instant
Text:no text Text:no text
K:spDamageTgtCP:2:Drawback$DamageYou/3:Orcish Cannonade deals 2 damage to target creature or player and 3 damage to you. 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.
K:Draw a card. SVar:DB1:DB$DealDamage | NumDmg$ 3 | Affected$ You | SubAbility$SVar=DB2
SVar:DB2:DB$Draw | NumCards$ 1
SVar:Rarity:Common SVar:Rarity:Common
SVar:Picture:http://www.wizards.com/global/images/magic/general/orcish_cannonade.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/orcish_cannonade.jpg
End End

View File

@@ -3,7 +3,8 @@ ManaCost:4 U
Types:Creature Illusion Types:Creature Illusion
Text:no text Text:no text
PT:2/2 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:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/psionic_entity.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/psionic_entity.jpg
End End

View File

@@ -20,6 +20,7 @@ public class AbilityFactory {
private boolean isAb = false; private boolean isAb = false;
private boolean isSp = false; private boolean isSp = false;
private boolean isDb = false;
public boolean isAbility() public boolean isAbility()
{ {
@@ -31,6 +32,10 @@ public class AbilityFactory {
return isSp; return isSp;
} }
public boolean isDrawback() {
return isDb;
}
private Ability_Cost abCost = null; private Ability_Cost abCost = null;
public Ability_Cost getAbCost() public Ability_Cost getAbCost()
@@ -120,14 +125,18 @@ public class AbilityFactory {
isSp = true; isSp = true;
API = mapParams.get("SP"); API = mapParams.get("SP");
} }
else if (mapParams.containsKey("DB")) {
isDb = true;
API = mapParams.get("DB");
}
else else
throw new RuntimeException("AbilityFactory : getAbility -- no API in " + hostCard.getName()); throw new RuntimeException("AbilityFactory : getAbility -- no API in " + hostCard.getName());
if (!isDb){
if (!mapParams.containsKey("Cost")) if (!mapParams.containsKey("Cost"))
throw new RuntimeException("AbilityFactory : getAbility -- no Cost in " + hostCard.getName()); throw new RuntimeException("AbilityFactory : getAbility -- no Cost in " + hostCard.getName());
abCost = new Ability_Cost(mapParams.get("Cost"), hostCard.getName(), isAb); abCost = new Ability_Cost(mapParams.get("Cost"), hostCard.getName(), isAb);
}
if (mapParams.containsKey("ValidTgts")) if (mapParams.containsKey("ValidTgts"))
{ {
@@ -145,8 +154,11 @@ public class AbilityFactory {
int min = mapParams.containsKey("TargetMin") ? Integer.parseInt(mapParams.get("TargetMin")) : 1; int min = mapParams.containsKey("TargetMin") ? Integer.parseInt(mapParams.get("TargetMin")) : 1;
int max = mapParams.containsKey("TargetMax") ? Integer.parseInt(mapParams.get("TargetMax")) : 1; int max = mapParams.containsKey("TargetMax") ? Integer.parseInt(mapParams.get("TargetMax")) : 1;
if (hasValid) if (hasValid){
abTgt = new Target(mapParams.get("TgtPrompt"), mapParams.get("ValidTgts").split(","), min, max); // 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 else
abTgt = new Target(mapParams.get("Tgt"), min, max); abTgt = new Target(mapParams.get("Tgt"), min, max);
@@ -161,29 +173,18 @@ public class AbilityFactory {
// *********************************** // ***********************************
// Match API keywords // Match API keywords
if (API.equals("DealDamage")) if (API.equals("DealDamage"))
{ {
final int NumDmg[] = {-1}; AbilityFactory_DealDamage dd = new AbilityFactory_DealDamage(this);
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]?")) if (isAb)
NumDmg[0] = Integer.parseInt(tmpND); SA = dd.getAbility();
}
AbilityFactory_DealDamage dd = new AbilityFactory_DealDamage();
if (isAb)
SA = dd.getAbility(this, NumDmg[0], NumDmgX[0]);
else if (isSp) 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 (API.equals("PutCounter")){
if (isAb) if (isAb)
@@ -240,9 +241,12 @@ public class AbilityFactory {
if (API.equals("GainLife")){ if (API.equals("GainLife")){
if (isAb) if (isAb)
SA = AbilityFactory_AlterLife.createAbilityGainLife(this); SA = AbilityFactory_AlterLife.createAbilityGainLife(this);
if (isSp){ else if (isSp)
SA = AbilityFactory_AlterLife.createSpellGainLife(this); SA = AbilityFactory_AlterLife.createSpellGainLife(this);
} else if (isDb)
SA = AbilityFactory_AlterLife.createSpellGainLife(this);
if(hasSubAbility())
SA.setSubAbility(getSubAbility());
} }
if (API.equals("LoseLife")){ if (API.equals("LoseLife")){
@@ -297,17 +301,23 @@ public class AbilityFactory {
if (API.equals("Draw")){ if (API.equals("Draw")){
if (isAb) if (isAb)
SA = AbilityFactory_ZoneAffecting.createAbilityDraw(this); SA = AbilityFactory_ZoneAffecting.createAbilityDraw(this);
if (isSp){ else if (isSp)
SA = AbilityFactory_ZoneAffecting.createSpellDraw(this); SA = AbilityFactory_ZoneAffecting.createSpellDraw(this);
} else if (isDb)
SA = AbilityFactory_ZoneAffecting.createDrawbackDraw(this);
if(hasSubAbility())
SA.setSubAbility(getSubAbility());
} }
if (API.equals("Mill")){ if (API.equals("Mill")){
if (isAb) if (isAb)
SA = AbilityFactory_ZoneAffecting.createAbilityMill(this); SA = AbilityFactory_ZoneAffecting.createAbilityMill(this);
if (isSp){ else if (isSp)
SA = AbilityFactory_ZoneAffecting.createSpellMill(this); SA = AbilityFactory_ZoneAffecting.createSpellMill(this);
} else if (isDb)
SA = AbilityFactory_ZoneAffecting.createDrawbackMill(this);
if(hasSubAbility())
SA.setSubAbility(getSubAbility());
} }
if (API.equals("Destroy")){ if (API.equals("Destroy")){
@@ -402,6 +412,27 @@ public class AbilityFactory {
return SA; 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;
}
} }

View File

@@ -18,15 +18,7 @@ public class AbilityFactory_AlterLife {
@Override @Override
public String getStackDescription(){ public String getStackDescription(){
// when getStackDesc is called, just build exactly what is happening // when getStackDesc is called, just build exactly what is happening
StringBuilder sb = new StringBuilder(); return gainLifeStackDescription(af, this);
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();
} }
public boolean canPlay(){ public boolean canPlay(){
@@ -59,15 +51,7 @@ public class AbilityFactory_AlterLife {
@Override @Override
public String getStackDescription(){ public String getStackDescription(){
// when getStackDesc is called, just build exactly what is happening // when getStackDesc is called, just build exactly what is happening
StringBuilder sb = new StringBuilder(); return gainLifeStackDescription(af, this);
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();
} }
public boolean canPlay(){ public boolean canPlay(){
@@ -93,6 +77,42 @@ public class AbilityFactory_AlterLife {
return spGainLife; 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<String,String> 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){ public static SpellAbility createAbilityLoseLife(final AbilityFactory AF){
final SpellAbility abLoseLife = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()){ final SpellAbility abLoseLife = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()){
private static final long serialVersionUID = 1129762905315395160L; private static final long serialVersionUID = 1129762905315395160L;
@@ -182,13 +202,6 @@ public class AbilityFactory_AlterLife {
return abLoseLife; 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){ public static int calculateAmount(Card card, String lifeAmount, SpellAbility ability){
if (lifeAmount.matches("X")) if (lifeAmount.matches("X"))
@@ -212,6 +225,29 @@ public class AbilityFactory_AlterLife {
return Integer.parseInt(lifeAmount); 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){ public static boolean gainLifeCanPlayAI(final AbilityFactory af, final SpellAbility sa, final String amountStr){
Random r = new Random(); Random r = new Random();
Ability_Cost abCost = sa.getPayCosts(); 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){ public static void gainLifeResolve(final AbilityFactory af, final SpellAbility sa, int lifeAmount){
HashMap<String,String> params = af.getMapParams(); HashMap<String,String> params = af.getMapParams();
String DrawBack = params.get("SubAbility");
Card card = af.getHostCard(); Card card = af.getHostCard();
ArrayList<Player> tgtPlayers; ArrayList<Player> tgtPlayers;
@@ -277,8 +312,20 @@ public class AbilityFactory_AlterLife {
if (tgt == null || p.canTarget(af.getHostCard())) if (tgt == null || p.canTarget(af.getHostCard()))
p.gainLife(lifeAmount, sa.getSourceCard()); 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){ public static boolean loseLifeCanPlayAI(final AbilityFactory af, final SpellAbility sa, final String amountStr){

View File

@@ -1,316 +1,397 @@
package forge;
import java.util.ArrayList; package forge;
import java.util.Random;
public class AbilityFactory_DealDamage { import java.util.ArrayList;
import java.util.Random;
private AbilityFactory AF = null; public class AbilityFactory_DealDamage {
private AbilityFactory AF = null;
private int nDamage = -1; private int nDamage = -1;
private String XDamage = "none"; private String XDamage = "none";
private boolean TgtOpp = false; private boolean TgtOpp = false;
public SpellAbility getAbility(final AbilityFactory af, final int NumDmg, final String NumDmgX) private Ability_Sub subAbAF = null;
{ private boolean hasSubAbAF = false;
AF = af; private String subAbStr = "none";
nDamage = NumDmg; private boolean hasSubAbStr = false;
XDamage = NumDmgX;
if(af.getMapParams().containsKey("Tgt")) public Ability_Sub getSubAbility() { return subAbAF; }
if (AF.getMapParams().get("Tgt").equals("TgtOpp"))
TgtOpp = true;
final SpellAbility abDamage = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) public AbilityFactory_DealDamage(AbilityFactory newAF)
{ {
private static final long serialVersionUID = -7560349014757367722L; AF = newAF;
@Override String tmpND = AF.getMapParams().get("NumDmg");
public boolean canPlay(){ if (tmpND.length() > 0)
return super.canPlay(); {
if (tmpND.matches("[xX]"))
XDamage = AF.getHostCard().getSVar(tmpND.substring(1));
else if (tmpND.matches("[0-9][0-9]?"))
nDamage = Integer.parseInt(tmpND);
} }
@Override if(AF.getMapParams().containsKey("Tgt"))
public boolean canPlayAI() { if (AF.getMapParams().get("Tgt").equals("TgtOpp"))
return doCanPlayAI(this); 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;
}
} }
@Override }
public String getStackDescription(){
return damageStackDescription(AF, this);
}
@Override public SpellAbility getAbility()
public void resolve() { {
doResolve(this); final SpellAbility abDamage = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt())
AF.getHostCard().setAbilityUsed(AF.getHostCard().getAbilityUsed() + 1); {
private static final long serialVersionUID = -7560349014757367722L;
} @Override
};//Ability_Activated public boolean canPlay(){
return super.canPlay();
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);
}
}; // 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;
} }
@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) 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())
{ {
Random r = new Random(); // prevent run-away activations // temporarily disabled until better AI
if(r.nextFloat() <= Math.pow(.6667, AF.getHostCard().getAbilityUsed())) if (AF.getAbCost().getSacCost()) return false;
rr = true; 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;
} }
Target tgt = AF.getAbTgt(); private String damageStackDescription(AbilityFactory af, SpellAbility sa){
// AI handle multi-targeting? // when damageStackDescription is called, just build exactly what is happening
tgt.resetTargets(); StringBuilder sb = new StringBuilder();
// target loop String name = af.getHostCard().getName();
while(tgt.getNumTargeted() < tgt.getMaxTargets()){ int damage = getNumDamage(sa);
// TODO: Consider targeting the planeswalker
if(tgt.canTgtCreatureAndPlayer()) {
if(shouldTgtP(damage)) { ArrayList<Object> tgts = findTargets(sa);
tgt.addTarget(AllZone.HumanPlayer);
continue;
}
Card c = chooseTgtC(damage); if (!(sa instanceof Ability_Sub))
if(c != null) { sb.append(name).append(" - ");
tgt.addTarget(c);
continue;
}
}
if(tgt.canTgtPlayer() || TgtOpp) { sb.append("Deals ").append(damage).append(" damage to ");
tgt.addTarget(AllZone.HumanPlayer); for(int i = 0; i < tgts.size(); i++){
continue; if (i != 0)
} sb.append(" ");
if(tgt.canTgtCreature()) { Object o = tgts.get(0);
Card c = chooseTgtC(damage); if (o instanceof Player){
if(c != null) { sb.append(((Player)o).getName());
tgt.addTarget(c); }
continue; else{
} sb.append(((Card)o).getName());
} }
// 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; }
sb.append(". ");
if (hasSubAbAF){
subAbAF.setParent(sa);
sb.append(subAbAF.getStackDescription());
}
return sb.toString();
}
private ArrayList<Object> findTargets(SpellAbility saMe){
Target tgt = AF.getAbTgt();
ArrayList<Object> tgts;
if (tgt != null)
tgts = tgt.getTargets();
else{
tgts = new ArrayList<Object>();
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;
}
private void doResolve(SpellAbility saMe)
{
int damage = getNumDamage(saMe);
ArrayList<Object> 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<Object> tgts;
Target tgt = AF.getAbTgt();
if (tgt != null)
tgts = tgt.getTargets();
else{
tgts = new ArrayList<Object>();
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<Object> tgts;
Target tgt = AF.getAbTgt();
if (tgt != null)
tgts = tgt.getTargets();
else{
tgts = new ArrayList<Object>();
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);
}
}

View File

@@ -14,17 +14,7 @@ public class AbilityFactory_ZoneAffecting {
@Override @Override
public String getStackDescription(){ public String getStackDescription(){
// when getStackDesc is called, just build exactly what is happening // when getStackDesc is called, just build exactly what is happening
Player player = af.getAbTgt() == null ? getActivatingPlayer() : getTargetPlayer(); return drawStackDescription(af, this);
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();
} }
public boolean canPlay(){ public boolean canPlay(){
@@ -55,17 +45,7 @@ public class AbilityFactory_ZoneAffecting {
@Override @Override
public String getStackDescription(){ public String getStackDescription(){
// when getStackDesc is called, just build exactly what is happening // when getStackDesc is called, just build exactly what is happening
Player player = af.getAbTgt() == null ? getActivatingPlayer() : getTargetPlayer(); return drawStackDescription(af, this);
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();
} }
public boolean canPlay(){ public boolean canPlay(){
@@ -87,6 +67,56 @@ public class AbilityFactory_ZoneAffecting {
return spDraw; 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){ public static boolean drawCanPlayAI(final AbilityFactory af, SpellAbility sa){
// AI cannot use this properly until he can use SAs during Humans turn // AI cannot use this properly until he can use SAs during Humans turn
if (!ComputerUtil.canPayCost(sa)) if (!ComputerUtil.canPayCost(sa))
@@ -161,13 +191,28 @@ public class AbilityFactory_ZoneAffecting {
} }
for(Player p : tgtPlayers) for(Player p : tgtPlayers)
if (tgt == null || p.canTarget(af.getHostCard())) if (tgt == null || p.canTarget(af.getHostCard())){
p.drawCards(numCards); if (params.containsKey("NextUpkeep"))
for(int i = 0; i < numCards; i++)
p.addSlowtripList(source);
else
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 (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 @Override
public String getStackDescription(){ public String getStackDescription(){
// when getStackDesc is called, just build exactly what is happening return millStackDescription(this, af);
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();
} }
public boolean canPlay(){ public boolean canPlay(){
@@ -220,6 +254,11 @@ public class AbilityFactory_ZoneAffecting {
final AbilityFactory af = AF; final AbilityFactory af = AF;
@Override
public String getStackDescription(){
return millStackDescription(this, af);
}
public boolean canPlay(){ public boolean canPlay(){
// super takes care of AdditionalCosts // super takes care of AdditionalCosts
return super.canPlay(); return super.canPlay();
@@ -239,6 +278,61 @@ public class AbilityFactory_ZoneAffecting {
return spMill; 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){ public static boolean millCanPlayAI(final AbilityFactory af, SpellAbility sa){
// AI cannot use this properly until he can use SAs during Humans turn // AI cannot use this properly until he can use SAs during Humans turn
if (!ComputerUtil.canPayCost(sa)) if (!ComputerUtil.canPayCost(sa))
@@ -304,6 +398,7 @@ public class AbilityFactory_ZoneAffecting {
HashMap<String,String> params = af.getMapParams(); HashMap<String,String> params = af.getMapParams();
Card source = sa.getSourceCard(); 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")); int numCards = Integer.parseInt(params.get("NumCards"));
ArrayList<Player> tgtPlayers; ArrayList<Player> tgtPlayers;
@@ -320,9 +415,18 @@ public class AbilityFactory_ZoneAffecting {
if (tgt == null || p.canTarget(af.getHostCard())) if (tgt == null || p.canTarget(af.getHostCard()))
p.mill(numCards); p.mill(numCards);
String DrawBack = params.get("SubAbility"); if (af.hasSubAbility()){
if (af.hasSubAbility()) Ability_Sub abSub = sa.getSubAbility();
CardFactoryUtil.doDrawBack(DrawBack, 0, source.getController(), source.getController().getOpponent(), tgtPlayers.get(0), source, null, sa); 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);
}
}
} }
} }

View File

@@ -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;
}
}

View File

@@ -440,32 +440,7 @@ public class MagicStack extends MyObservable {
AllZone.Phase.resetPriority(); // ActivePlayer gains priority first after Resolve AllZone.Phase.resetPriority(); // ActivePlayer gains priority first after Resolve
Card source = sa.getSourceCard(); Card source = sa.getSourceCard();
boolean fizzle = false; boolean fizzle = hasFizzled(sa, source);
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<Object> 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);
}
if (!fizzle) { if (!fizzle) {
final Card crd = source; final Card crd = source;
@@ -546,6 +521,42 @@ public class MagicStack extends MyObservable {
GuiDisplayUtil.updateGUI(); 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<Object> 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() { public SpellAbility pop() {
SpellAbility sp = (SpellAbility) stack.remove(0); SpellAbility sp = (SpellAbility) stack.remove(0);
this.updateObservers(); this.updateObservers();

View File

@@ -48,6 +48,7 @@ public abstract class SpellAbility {
protected Target chosenTarget = null; protected Target chosenTarget = null;
private SpellAbility_Restriction restrictions = new SpellAbility_Restriction(); private SpellAbility_Restriction restrictions = new SpellAbility_Restriction();
private Ability_Sub subAbility = null;
private CardList sacrificedCards = null; private CardList sacrificedCards = null;
@@ -322,6 +323,14 @@ public abstract class SpellAbility {
return description; return description;
} }
public void setSubAbility(Ability_Sub subAbility) {
this.subAbility = subAbility;
}
public Ability_Sub getSubAbility() {
return this.subAbility;
}
public Card getTargetCard() { public Card getTargetCard() {
if(targetCard == null){ if(targetCard == null){
Target tgt = this.getTarget(); Target tgt = this.getTarget();

View File

@@ -28,13 +28,14 @@ public class SpellAbility_Requirements {
// freeze Stack. No abilities should go onto the stack while I'm filling requirements. // freeze Stack. No abilities should go onto the stack while I'm filling requirements.
AllZone.Stack.freezeStack(); 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.setRequirements(this);
select.resetTargets(); select.resetTargets();
select.chooseTargets(); select.chooseTargets();
} }
else
startPaying();
} }
public void finishedTargeting(){ public void finishedTargeting(){

View File

@@ -47,9 +47,23 @@ public class Target_Selection {
req.finishedTargeting(); req.finishedTargeting();
return false; return false;
} }
else if (bDoneTarget && target.isMinTargetsChosen() || target.isMaxTargetsChosen()){ else if (!doesTarget() || bDoneTarget && target.isMinTargetsChosen() || target.isMaxTargetsChosen()){
req.finishedTargeting(); Ability_Sub abSub = ability.getSubAbility();
return true;
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 //targets still needed