From b99c9863933686356e1e0d63a1bea2bc22c15c5f Mon Sep 17 00:00:00 2001 From: jendave Date: Sat, 6 Aug 2011 06:13:55 +0000 Subject: [PATCH] - Added PayLife as a new kind of Ability_Cost - Pulled targeting from Ability_Cost into it's own class. - Added Player info to PlayerLife - Added Reckless Assault as a sample of PayLife<> cost. --- .gitattributes | 3 + res/cards.txt | 7 + src/forge/Ability_Cost.java | 86 ++---- src/forge/AllZone.java | 4 +- src/forge/CardFactory.java | 57 ++-- src/forge/ComputerUtil.java | 9 + src/forge/Cost_Payment.java | 331 ++++++++--------------- src/forge/GameAction.java | 5 +- src/forge/Input_PayCostMana.java | 2 +- src/forge/PlayerLife.java | 42 +-- src/forge/SpellAbility.java | 9 + src/forge/SpellAbility_Requirements.java | 74 +++++ src/forge/Target.java | 60 ++++ src/forge/Target_Selection.java | 182 +++++++++++++ 14 files changed, 540 insertions(+), 331 deletions(-) create mode 100644 src/forge/SpellAbility_Requirements.java create mode 100644 src/forge/Target.java create mode 100644 src/forge/Target_Selection.java diff --git a/.gitattributes b/.gitattributes index a6943c08b6f..a51f1ce234c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -557,12 +557,15 @@ src/forge/Spell.java svneol=native#text/plain src/forge/SpellAbility.java -text svneol=native#text/plain src/forge/SpellAbilityList.java svneol=native#text/plain src/forge/SpellAbilityUtil.java svneol=native#text/plain +src/forge/SpellAbility_Requirements.java -text svneol=native#text/plain src/forge/Spell_Evoke.java svneol=native#text/plain src/forge/Spell_Permanent.java svneol=native#text/plain src/forge/StackObserver.java svneol=native#text/plain src/forge/StaticEffects.java -text svneol=native#text/plain src/forge/TableModel.java -text svneol=native#text/plain src/forge/TableSorter.java svneol=native#text/plain +src/forge/Target.java -text svneol=native#text/plain +src/forge/Target_Selection.java -text svneol=native#text/plain src/forge/TempRun.java svneol=native#text/plain src/forge/Test.java svneol=native#text/plain src/forge/TestMove.java svneol=native#text/plain diff --git a/res/cards.txt b/res/cards.txt index 54e67bfe37b..a12f2aa02ae 100644 --- a/res/cards.txt +++ b/res/cards.txt @@ -1,3 +1,10 @@ +Reckless Assault +2 B R +Enchantment +no text +abDamageTgtCP 1 PayLife<2>:1 +SVar:RemAIDeck:True + Disfigure B Instant diff --git a/src/forge/Ability_Cost.java b/src/forge/Ability_Cost.java index dd2cd79b278..cee76c8ddd4 100644 --- a/src/forge/Ability_Cost.java +++ b/src/forge/Ability_Cost.java @@ -1,25 +1,6 @@ package forge; public class Ability_Cost { - private boolean tgtPlayer = false; - public boolean canTgtPlayer() { return tgtPlayer; } - private boolean tgtCreature = false; - public boolean canTgtCreature() { return tgtCreature; } - - public boolean canTgtCreaturePlayer() { return tgtCreature && tgtPlayer; } - public boolean doesTarget() { return tgtCreature || tgtPlayer; } - - private int minTargets = 0; - public int getMinTargets() { return minTargets; } - private int maxTargets = 0; - public int getMaxTargets() { return maxTargets; } - // add array of targets here? - - private int numTargeted = 0; - public int getNumTargeted() { return numTargeted; } - public void incrementTargets() { numTargeted++; } - public void resetTargets() { numTargeted = 0; } - private boolean sacCost = false; public boolean getSacCost() { return sacCost; } private String sacType = ""; // or CARDNAME @@ -30,7 +11,7 @@ public class Ability_Cost { private boolean tapCost = false; public boolean getTap() { return tapCost; } - // future expansion of Ability_Cost class: untap, and lifeCost + // future expansion of Ability_Cost class: untap // private boolean untapCost = false; private boolean subtractCounterCost = false; @@ -44,7 +25,9 @@ public class Ability_Cost { public Counters getCounterType() { return counterType; } private boolean lifeCost = false; + public boolean getLifeCost() { return lifeCost; } private int lifeAmount = 0; + public int getLifeAmount() { return lifeAmount; } public boolean hasNoManaCost() { return manaCost.equals("") || manaCost.equals("0"); }; private String manaCost = ""; @@ -57,34 +40,8 @@ public class Ability_Cost { // when adding new costs for cost string, place them here name = cardName; - if (parse.contains("Tgt")){ - // Tgt{C}{P}{//} - int tgtPos = parse.indexOf("Tgt"); - int endTgt = parse.indexOf(" "); - //System.out.println(cardName); - String tgtStr = parse.substring(tgtPos, endTgt); - parse = parse.substring(endTgt+1); - tgtStr = tgtStr.replace("Tgt", ""); - String[] tgtSplit = tgtStr.split("/"); - - if (tgtSplit[0].contains("C")) - tgtCreature = true; - if (tgtSplit[0].contains("P")) - tgtPlayer = true; - //todo(sol) add Opp - - if (tgtSplit.length != 3){ - minTargets = 1; - maxTargets = 1; - } - else{ - minTargets = Integer.parseInt(tgtSplit[1]); - maxTargets = Integer.parseInt(tgtSplit[2]); - } - } - if(parse.contains("SubCounter<")) { - // SubCounter//counters removed + // SubCounter subtractCounterCost = true; int counterPos = parse.indexOf("SubCounter<"); int endPos = parse.indexOf(">", counterPos); @@ -99,12 +56,27 @@ public class Ability_Cost { counterAmount = Integer.parseInt(strSplit[1]); } + if(parse.contains("PayLife<")) { + // PayLife + lifeCost = true; + int lifePos = parse.indexOf("PayLife<"); + int endPos = parse.indexOf(">", lifePos); + String str = parse.substring(lifePos, endPos+1); + parse = parse.replace(str, "").trim(); + + str = str.replace("PayLife<", ""); + str = str.replace(">", ""); + + lifeAmount = Integer.parseInt(str); + } + if(parse.contains("Sac-")) { sacCost = true; int sacPos = parse.indexOf("Sac-"); sacType = parse.substring(sacPos).replace("Sac-", "").trim(); sacThis = (sacType.equals("CARDNAME")); - parse = parse.substring(0,sacPos-1).trim(); + if (sacPos > 0) + parse = parse.substring(0,sacPos-1).trim(); } if(parse.contains("T")) { @@ -156,8 +128,9 @@ public class Ability_Cost { if (caps) cost.append("Pay "); else - cost.append(", Pay"); + cost.append(", Pay "); cost.append(lifeAmount); + cost.append(" Life"); caps = false; } @@ -192,19 +165,4 @@ public class Ability_Cost { } return cost.toString(); } - - public String targetString() - { - String tgt = ""; - if (tgtCreature) - tgt += "creature"; - if (tgtPlayer && !tgt.equals("")) - tgt += " or "; - if (tgtPlayer) - tgt += "player"; - - tgt += "."; - - return "target " + tgt; - } } diff --git a/src/forge/AllZone.java b/src/forge/AllZone.java index 3847d44b564..c0aee6295e6 100644 --- a/src/forge/AllZone.java +++ b/src/forge/AllZone.java @@ -37,8 +37,8 @@ public class AllZone implements NewConstants { public static Combat Combat = new Combat(); public static Combat pwCombat = new Combat();//for Planeswalker combat - public static PlayerLife Human_Life = new PlayerLife(); - public static PlayerLife Computer_Life = new PlayerLife(); + public static PlayerLife Human_Life = new PlayerLife(Constant.Player.Human); + public static PlayerLife Computer_Life = new PlayerLife(Constant.Player.Computer); public static PlayerPoisonCounter Human_PoisonCounter = new PlayerPoisonCounter(); public static PlayerPoisonCounter Computer_PoisonCounter = new PlayerPoisonCounter(); diff --git a/src/forge/CardFactory.java b/src/forge/CardFactory.java index 840ce4d7ee4..a4621aae79b 100644 --- a/src/forge/CardFactory.java +++ b/src/forge/CardFactory.java @@ -1267,10 +1267,10 @@ public class CardFactory implements NewConstants { card.removeIntrinsicKeyword(parse); String k[] = parse.split(":"); + String tmpCost[] = k[0].replace("abAllPump", "").split(" ", 2); - String tmpCost = k[0].substring(9); - - final Ability_Cost abCost = new Ability_Cost(tmpCost, card.getName()); + final Target abTgt = new Target(tmpCost[0]); + final Ability_Cost abCost = new Ability_Cost(tmpCost[1], card.getName()); final String Scope[] = k[1].split("/"); @@ -1422,7 +1422,9 @@ public class CardFactory implements NewConstants { return false; // temporarily disabled until AI is improved - if (abCost.getSacCost()) return false; + if (abCost.getSacCost()) return false; + if (abCost.getSubCounter()) return false; + if (abCost.getLifeCost()) return false; if (abCost.getTap() && (card.isTapped() || card.isSick())) return false; @@ -1516,6 +1518,7 @@ public class CardFactory implements NewConstants { abAllPump.setDescription(abCost.toString() + spDesc[0]); abAllPump.setStackDescription(stDesc[0]); abAllPump.setPayCosts(abCost); + abAllPump.setTarget(abTgt); card.addSpellAbility(abAllPump); } @@ -1528,10 +1531,12 @@ public class CardFactory implements NewConstants { card.removeIntrinsicKeyword(parse); String k[] = parse.split(":"); - - String tmpCost = k[0].substring(6); - - final Ability_Cost abCost = new Ability_Cost(tmpCost, card.getName()); + String tmp = k[0].replace("abPump", ""); + + String[] tmpCost = tmp.split(" ", 2); + + final Target abTgt = new Target(tmpCost[0]); + final Ability_Cost abCost = new Ability_Cost(tmpCost[1], card.getName()); final int NumAttack[] = {-1138}; final String AttackX[] = {"none"}; @@ -1597,7 +1602,7 @@ public class CardFactory implements NewConstants { if((AttackX[0].equals("none") && !(NumAttack[0] == -1138)) && (DefenseX[0].equals("none") && !(NumDefense[0] == -1138)) && Keyword[0].equals("none")) { // pt boost - if(abCost.doesTarget()) sbD.append("Target creature gets "); + if(abTgt.doesTarget()) sbD.append("Target creature gets "); else { sbD.append(cardName); sbD.append(" gets "); @@ -1621,7 +1626,7 @@ public class CardFactory implements NewConstants { if((AttackX[0].equals("none") && NumAttack[0] == -1138) && (DefenseX[0].equals("none") && NumDefense[0] == -1138) && !Keyword[0].equals("none")) { // k boost - if(abCost.doesTarget()) sbD.append("Target creature gains "); + if(abTgt.doesTarget()) sbD.append("Target creature gains "); else { sbD.append(cardName); sbD.append(" gains "); @@ -1633,7 +1638,7 @@ public class CardFactory implements NewConstants { if((AttackX[0].equals("none") && !(NumAttack[0] == -1138)) && (DefenseX[0].equals("none") && !(NumDefense[0] == -1138)) && !Keyword[0].equals("none")) { // ptk boost - if(abCost.doesTarget()) sbD.append("Target creature gets "); + if(abTgt.doesTarget()) sbD.append("Target creature gets "); else { sbD.append(cardName); sbD.append(" gets "); @@ -1700,6 +1705,7 @@ public class CardFactory implements NewConstants { // temporarily disabled until AI is improved if (abCost.getSacCost()) return false; if (abCost.getSubCounter()) return false; + if (abCost.getLifeCost()) return false; if (!ComputerUtil.canPayCost(this)) return false; @@ -1709,7 +1715,7 @@ public class CardFactory implements NewConstants { if(AllZone.Phase.getPhase().equals(Constant.Phase.Main2)) return false; - if(!abCost.doesTarget()) { + if(!abTgt.doesTarget()) { setTargetCard(card); if((card.getNetDefense() + defense > 0) && (!card.getKeyword().contains(keyword))) { @@ -1778,9 +1784,9 @@ public class CardFactory implements NewConstants { @Override public void resolve() { if(AllZone.GameAction.isCardInPlay(getTargetCard()) - && (CardFactoryUtil.canTarget(card, getTargetCard()) || !abCost.doesTarget() )) { + && (CardFactoryUtil.canTarget(card, getTargetCard()) || !abTgt.doesTarget() )) { final Card[] creature = new Card[1]; - if(abCost.doesTarget()) creature[0] = getTargetCard(); + if(abTgt.doesTarget()) creature[0] = getTargetCard(); else creature[0] = card; final int a = getNumAttack(); @@ -1829,8 +1835,10 @@ public class CardFactory implements NewConstants { ability.setDescription(spDesc[0]); ability.setStackDescription(stDesc[0]); - if(!abCost.doesTarget()) + if(!abTgt.doesTarget()) ability.setTargetCard(card); + else + ability.setTarget(abTgt); card.addSpellAbility(ability); } @@ -2264,10 +2272,11 @@ public class CardFactory implements NewConstants { String parse = card.getKeyword().get(n).toString(); card.removeIntrinsicKeyword(parse); - String k[] = parse.split(":"); - String tmpCost = k[0].substring(8); - - final Ability_Cost abCost = new Ability_Cost(tmpCost, card.getName()); + String k[] = parse.split(":"); + String tmpCost[] = k[0].replace("abDamage", "").split(" ", 2); + + final Target abTgt = new Target(tmpCost[0]); + final Ability_Cost abCost = new Ability_Cost(tmpCost[1], card.getName()); final int NumDmg[] = {-1}; final String NumDmgX[] = {"none"}; @@ -2301,7 +2310,7 @@ public class CardFactory implements NewConstants { sb.append(card.getName()); sb.append(" deals " + NumDmg[0] + " damage to "); - sb.append(abCost.targetString()); + sb.append(abTgt.targetString()); spDesc[0] = sb.toString(); stDesc[0] = card.getName() + " -" + sb.toString(); } @@ -2380,6 +2389,7 @@ public class CardFactory implements NewConstants { // temporarily disabled until better AI if (abCost.getSacCost()) return false; if (abCost.getSubCounter()) return false; + if (abCost.getLifeCost()) return false; if (!ComputerUtil.canPayCost(this)) return false; @@ -2391,7 +2401,7 @@ public class CardFactory implements NewConstants { if(r.nextFloat() <= Math.pow(.6667, card.getAbilityUsed())) rr = true; - if(abCost.canTgtCreaturePlayer()) { + if(abTgt.canTgtCreaturePlayer()) { if(shouldTgtP()) { setTargetPlayer(Constant.Player.Human); return rr; @@ -2404,12 +2414,12 @@ public class CardFactory implements NewConstants { } } - if(abCost.canTgtPlayer()/* || TgtOpp[0] == true */) { + if(abTgt.canTgtPlayer()/* || TgtOpp[0] == true */) { setTargetPlayer(Constant.Player.Human); return rr; } - if(abCost.canTgtCreature()) { + if(abTgt.canTgtCreature()) { Card c = chooseTgtC(); if(c != null) { setTargetCard(c); @@ -2453,6 +2463,7 @@ public class CardFactory implements NewConstants { };//Ability_Activated abDamage.setPayCosts(abCost); + abDamage.setTarget(abTgt); abDamage.setDescription(spDesc[0]); abDamage.setStackDescription(stDesc[0]); diff --git a/src/forge/ComputerUtil.java b/src/forge/ComputerUtil.java index a9fb7dd31d9..9f5e5606b25 100644 --- a/src/forge/ComputerUtil.java +++ b/src/forge/ComputerUtil.java @@ -31,6 +31,7 @@ public class ComputerUtil AllZone.Computer_Hand.remove(all[i].getSourceCard()); Ability_Cost cost = all[i].getPayCosts(); + Target tgt = all[i].getTarget(); if (cost == null){ if(all[i] instanceof Ability_Tap) @@ -43,6 +44,9 @@ public class ComputerUtil AllZone.Stack.add(all[i]); } else{ + if (tgt != null && tgt.doesTarget()) + all[i].chooseTargetAI(); + Cost_Payment pay = new Cost_Payment(cost, all[i]); pay.payComputerCosts(); } @@ -231,6 +235,11 @@ public class ComputerUtil return false; } } + + if (cost.getLifeCost()){ + if (AllZone.GameAction.getPlayerLife(Constant.Player.Computer).getLife() <= cost.getLifeAmount()) + return false; + } // check additional costs. if (cost.getSacCost()){ diff --git a/src/forge/Cost_Payment.java b/src/forge/Cost_Payment.java index 3b168d956f4..036f87f4e4b 100644 --- a/src/forge/Cost_Payment.java +++ b/src/forge/Cost_Payment.java @@ -1,23 +1,28 @@ package forge; +import javax.swing.JOptionPane; + public class Cost_Payment { - Ability_Cost cost = null; - SpellAbility ability = null; - Card card = null; + private Ability_Cost cost = null; + private SpellAbility ability = null; + private Card card = null; + private SpellAbility_Requirements req = null; public Ability_Cost getCost() { return cost; } public SpellAbility getAbility() { return ability; } + public Card getCard() { return card; } + + public void setRequirements(SpellAbility_Requirements reqs) { req = reqs; } public void setCancel(boolean cancel) { bCancel = cancel; } - public void setDoneTarget(boolean done) { bCancel = done; } + public boolean isCanceled() { return bCancel; } private boolean payTap = false; private boolean payMana = false; private boolean paySubCounter = false; private boolean paySac = false; + private boolean payLife = false; + private boolean bCancel = false; - private boolean bCasting = false; - private boolean bDoneTarget = false; - private PlayerZone fromZone = null; public void setPayMana(boolean bPay){ payMana = bPay; } public void setPaySac(boolean bSac){ paySac = bSac; } @@ -33,6 +38,7 @@ public class Cost_Payment { payMana = cost.hasNoManaCost(); paySubCounter = !cost.getSubCounter(); paySac = !cost.getSacCost(); + payLife = !cost.getLifeCost(); } public boolean canPayAdditionalCosts(){ @@ -48,6 +54,12 @@ public class Cost_Payment { } } + if (cost.getLifeCost()){ + int curLife = AllZone.GameAction.getPlayerLife(card.getController()).getLife(); + if (curLife < cost.getLifeAmount()) + return false; + } + if (cost.getSacCost()){ if (!cost.getSacThis()){ PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController()); @@ -64,30 +76,11 @@ public class Cost_Payment { } public boolean payCost(){ - if (bCancel || bDoneTarget && cost.getNumTargeted() < cost.getMinTargets()){ - cancelPayment(); + if (bCancel){ + req.finishPaying(); return false; } - if (ability instanceof Spell && !bCasting){ - // remove from hand, todo(sol) be careful of spell copies when spells start using this - bCasting = true; - Card c = ability.getSourceCard(); - fromZone = AllZone.getZone(c); - fromZone.remove(c); - } - - // targetting, with forward code for multiple target abilities - if (!bDoneTarget && cost.getMinTargets() > 0 && cost.getNumTargeted() < cost.getMaxTargets()){ - if (cost.canTgtCreature() && cost.canTgtPlayer()) - changeInput.stopSetNext(targetCreaturePlayer(ability, Command.Blank, true, this)); - else if(cost.canTgtCreature()) - changeInput.stopSetNext(targetCreature(ability, this)); - else if(cost.canTgtPlayer()) - changeInput.stopSetNext(targetPlayer(ability, this)); - return false; - } - if (!payTap && cost.getTap()){ if (card.isUntapped()){ card.tap(); @@ -98,13 +91,12 @@ public class Cost_Payment { } // insert untap here - if (!payMana && !cost.hasNoManaCost()){ - // pay mana here + + if (!payMana && !cost.hasNoManaCost()){ // pay mana here changeInput.stopSetNext(new Input_PayCostMana(this)); return false; } - if (!paySubCounter && cost.getSubCounter()){ - // subtract counters here. + if (!paySubCounter && cost.getSubCounter()){ // pay counters here. Counters c = cost.getCounterType(); int countersLeft = card.getCounters(c) - cost.getCounterNum(); if (countersLeft >= 0){ @@ -112,12 +104,35 @@ public class Cost_Payment { paySubCounter = true; } else{ - cancelPayment(); + bCancel = true; + req.finishPaying(); return false; } } - if (!paySac && cost.getSacCost()) - { + + if (!payLife && cost.getLifeCost()){ // pay life here + // todo: should ask with a yes/no popup box to pay life? + StringBuilder sb = new StringBuilder(); + sb.append(getCard().getName()); + sb.append(" - Pay "); + sb.append(cost.getLifeAmount()); + sb.append(" Life?"); + Object[] possibleValues = {"Yes", "No"}; + Object choice = JOptionPane.showOptionDialog(null, sb.toString(), getCard().getName() + " - Cost", + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, + null, possibleValues, possibleValues[0]); + if(choice.equals(0)) { + AllZone.GameAction.getPlayerLife(card.getController()).payLife(cost.getLifeAmount()); + payLife = true; + } + else{ + bCancel = true; + req.finishPaying(); + return false; + } + } + + if (!paySac && cost.getSacCost()){ // sacrifice stuff here if (cost.getSacThis()) changeInput.stopSetNext(sacrificeThis(ability, this)); @@ -126,28 +141,16 @@ public class Cost_Payment { return false; } - if (isAllPaid()) - allPaid(); + req.finishPaying(); return true; } public boolean isAllPaid(){ - return (payTap && payMana && paySubCounter && paySac); - } - - public void allPaid(){ - AllZone.ManaPool.clearPay(false); - AllZone.Stack.add(ability); - cost.resetTargets(); + return (payTap && payMana && paySubCounter && paySac && payLife); } public void cancelPayment(){ // unpay anything we can. - cost.resetTargets(); - if (bCasting){ - // add back to hand - fromZone.add(ability.getSourceCard()); - } if (cost.getTap() && payTap){ // untap if tapped card.untap(); @@ -162,8 +165,60 @@ public class Cost_Payment { card.setCounter(c, countersLeft); } + // refund life + if (cost.getLifeCost() && payLife){ + PlayerLife life = AllZone.GameAction.getPlayerLife(card.getController()); + life.payLife(cost.getLifeAmount()*-1); + } + // can't really unsacrifice things } + + public void payComputerCosts(){ + Card sacCard = null; + ability.setActivatingPlayer(Constant.Player.Computer); + + // double check if something can be sacrificed here. Real check is in ComputerUtil.canPayAdditionalCosts() + if (cost.getSacCost()){ + if (cost.getSacThis()) + sacCard = card; + else + sacCard = ComputerUtil.chooseSacrificeType(cost.getSacType(), card, ability.getTargetCard()); + + if (sacCard == null){ + System.out.println("Couldn't find a valid card to sacrifice for: "+card.getName()); + return; + } + } + // double check if counters available? Real check is in ComputerUtil.canPayAdditionalCosts() + int countersLeft = 0; + if (cost.getSubCounter()){ + Counters c = cost.getCounterType(); + countersLeft = card.getCounters(c) - cost.getCounterNum(); + if (countersLeft < 0){ + System.out.println("Not enough " + c.getName() + " on "+card.getName()); + return; + } + } + + if (cost.getTap()) + card.tap(); + + if (!cost.hasNoManaCost()) + ComputerUtil.payManaCost(ability); + + if (cost.getSubCounter()) + card.setCounter(cost.getCounterType(), countersLeft); + + if (cost.getLifeCost()) + AllZone.GameAction.getPlayerLife(card.getController()).payLife(cost.getLifeAmount()); + + if (cost.getSacCost()) + AllZone.GameAction.sacrifice(sacCard); + + AllZone.Stack.add(ability); + } + public static Input sacrificeThis(final SpellAbility spell, final Cost_Payment payment) { Input target = new Input() { @@ -172,10 +227,15 @@ public class Cost_Payment { @Override public void showMessage() { Card card = spell.getSourceCard(); - String[] choices = {"Yes", "No"}; if(card.getController().equals(Constant.Player.Human)) { - Object o = AllZone.Display.getChoice("Sacrifice " + card.getName() + " ?", choices); - if(o.equals("Yes")) { + StringBuilder sb = new StringBuilder(); + sb.append(card.getName()); + sb.append(" - Sacrifice?"); + Object[] possibleValues = {"Yes", "No"}; + Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, + null, possibleValues, possibleValues[0]); + if(choice.equals(0)) { AllZone.GameAction.sacrifice(card); payment.setPaySac(true); stop(); @@ -226,171 +286,4 @@ public class Cost_Payment { }; return target; }//sacrificeType() - - public static Input targetCreaturePlayer(final SpellAbility ability, final Command paid, final boolean targeted, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 2781418414287281005L; - - @Override - public void showMessage() { - AllZone.Display.showMessage("Select target Creature, Player, or Planeswalker"); - // when multi targets (Arc Mage) are added, need this: - // if payment.targeted < mintarget only enable cancel - // else if payment.targeted < maxtarget enable cancel and ok - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - - @Override - public void selectButtonOK() { - payment.setDoneTarget(true); - stop(); - payment.payCost(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if((card.isCreature() || card.isPlaneswalker()) && zone.is(Constant.Zone.Play) - && (!targeted || CardFactoryUtil.canTarget(ability, card))) { - ability.setTargetCard(card); - done(); - } - }//selectCard() - - @Override - public void selectPlayer(String player) { - ability.setTargetPlayer(player); - done(); - } - - void done() { - payment.getCost().incrementTargets(); - paid.execute(); - stop(); - payment.payCost(); - } - }; - return target; - }//input_targetCreaturePlayer() - - public static Input targetCreature(final SpellAbility ability, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 2781418414287281005L; - - @Override - public void showMessage() { - AllZone.Display.showMessage("Select target Creature"); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if(card.isCreature() && zone.is(Constant.Zone.Play) && (CardFactoryUtil.canTarget(ability, card))) { - ability.setTargetCard(card); - done(); - } - }//selectCard() - - void done() { - payment.getCost().incrementTargets(); - stop(); - payment.payCost(); - } - }; - return target; - }//targetCreature() - - public static Input targetPlayer(final SpellAbility ability, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 2781418414287281005L; - - @Override - public void showMessage() { - AllZone.Display.showMessage("Select target Player or Planeswalker"); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if(card.isPlaneswalker() && zone.is(Constant.Zone.Play) && (!CardFactoryUtil.canTarget(ability, card))) { - ability.setTargetCard(card); - done(); - } - }//selectCard() - - @Override - public void selectPlayer(String player) { - ability.setTargetPlayer(player); - done(); - } - - void done() { - payment.getCost().incrementTargets(); - stop(); - payment.payCost(); - } - }; - return target; - }//targetPlayer() - - public void payComputerCosts(){ - Card sacCard = null; - ability.setActivatingPlayer(Constant.Player.Computer); - if (cost.doesTarget()) - ability.chooseTargetAI(); - - // double check if something can be sacrificed here. Real check is in ComputerUtil.canPayAdditionalCosts() - if (cost.getSacCost()){ - if (cost.getSacThis()) - sacCard = card; - else - sacCard = ComputerUtil.chooseSacrificeType(cost.getSacType(), card, ability.getTargetCard()); - - if (sacCard == null){ - System.out.println("Couldn't find a valid card to sacrifice for: "+card.getName()); - return; - } - } - // double check if counters available? Real check is in ComputerUtil.canPayAdditionalCosts() - int countersLeft = 0; - if (cost.getSubCounter()){ - Counters c = cost.getCounterType(); - countersLeft = card.getCounters(c) - cost.getCounterNum(); - if (countersLeft < 0){ - System.out.println("Not enough " + c.getName() + " on "+card.getName()); - return; - } - } - - if (cost.getTap()) - card.tap(); - if (!cost.hasNoManaCost()) - ComputerUtil.payManaCost(ability); - if (cost.getSubCounter()) - card.setCounter(cost.getCounterType(), countersLeft); - if (cost.getSacCost()) - AllZone.GameAction.sacrifice(sacCard); - - AllZone.Stack.add(ability); - } } diff --git a/src/forge/GameAction.java b/src/forge/GameAction.java index 8eb6a03e544..04056aefd50 100644 --- a/src/forge/GameAction.java +++ b/src/forge/GameAction.java @@ -3456,8 +3456,11 @@ public class GameAction { public void playSpellAbility(SpellAbility sa) { sa.setActivatingPlayer(Constant.Player.Human); if (sa.getPayCosts() != null){ + Target_Selection ts = new Target_Selection(sa.getTarget(), sa); Cost_Payment payment = new Cost_Payment(sa.getPayCosts(), sa); - payment.payCost(); + + SpellAbility_Requirements req = new SpellAbility_Requirements(sa, ts, payment); + req.fillRequirements(); } else{ ManaCost manaCost = new ManaCost(sa.getManaCost()); diff --git a/src/forge/Input_PayCostMana.java b/src/forge/Input_PayCostMana.java index 1a7544df9ef..c2926731334 100644 --- a/src/forge/Input_PayCostMana.java +++ b/src/forge/Input_PayCostMana.java @@ -16,7 +16,7 @@ public class Input_PayCostMana extends Input { public Input_PayCostMana(Cost_Payment payment) { costPayment = payment; - originalManaCost = costPayment.cost.getMana(); + originalManaCost = costPayment.getCost().getMana(); sa = payment.getAbility(); if(Phase.GameBegins == 1) { diff --git a/src/forge/PlayerLife.java b/src/forge/PlayerLife.java index 89ff149c2c9..dac145076d7 100644 --- a/src/forge/PlayerLife.java +++ b/src/forge/PlayerLife.java @@ -3,12 +3,23 @@ public class PlayerLife extends MyObservable implements java.io.Serializable { private static final long serialVersionUID = -1614695903669967202L; + private String player; + private Card playerCard; private int life; private int assignedDamage;//from combat public void setAssignedDamage(int n) {assignedDamage = n;} public int getAssignedDamage() {return assignedDamage;} + public PlayerLife(String pl) + { + player = pl; + if (player.equals(Constant.Player.Human)) + playerCard = AllZone.CardFactory.HumanNullCard; + else + playerCard = AllZone.CardFactory.ComputerNullCard; + } + public int getLife() { return life; @@ -18,39 +29,28 @@ public class PlayerLife extends MyObservable implements java.io.Serializable life = life2; this.updateObservers(); } + public void addLife(int life2) { - Card WhoGainedLife = new Card(); - if(AllZone.Human_Life.getLife() != AllZone.Computer_Life.getLife()) { - if(AllZone.Human_Life.getLife() == life) WhoGainedLife = AllZone.CardFactory.HumanNullCard; - else WhoGainedLife = AllZone.CardFactory.ComputerNullCard; - } life += life2; - if(WhoGainedLife != AllZone.CardFactory.HumanNullCard && WhoGainedLife != AllZone.CardFactory.ComputerNullCard) { - if(AllZone.Human_Life.getLife() == life) WhoGainedLife = AllZone.CardFactory.HumanNullCard; - else WhoGainedLife = AllZone.CardFactory.ComputerNullCard; - } Object[] Life_Whenever_Parameters = new Object[1]; Life_Whenever_Parameters[0] = life2; - AllZone.GameAction.CheckWheneverKeyword(WhoGainedLife, "GainLife", Life_Whenever_Parameters); + AllZone.GameAction.CheckWheneverKeyword(playerCard, "GainLife", Life_Whenever_Parameters); this.updateObservers(); } public void subtractLife(int life2, Card SourceCard) { - Card WhoGainedLife = new Card(); - if(AllZone.Human_Life.getLife() != AllZone.Computer_Life.getLife()) { - if(AllZone.Human_Life.getLife() == life) WhoGainedLife = AllZone.CardFactory.HumanNullCard; - else WhoGainedLife = AllZone.CardFactory.ComputerNullCard; - } life -= life2; - if(WhoGainedLife != AllZone.CardFactory.HumanNullCard && WhoGainedLife != AllZone.CardFactory.ComputerNullCard) { - if(AllZone.Human_Life.getLife() == life) WhoGainedLife = AllZone.CardFactory.HumanNullCard; - else WhoGainedLife = AllZone.CardFactory.ComputerNullCard; - } Object[] DealsDamage_Whenever_Parameters = new Object[3]; - DealsDamage_Whenever_Parameters[0] = WhoGainedLife.getController(); + DealsDamage_Whenever_Parameters[0] = player; DealsDamage_Whenever_Parameters[2] = SourceCard; - AllZone.GameAction.CheckWheneverKeyword(WhoGainedLife, "DealsDamage", DealsDamage_Whenever_Parameters); + AllZone.GameAction.CheckWheneverKeyword(playerCard, "DealsDamage", DealsDamage_Whenever_Parameters); + this.updateObservers(); + } + + public void payLife(int life2) + { + life -= life2; this.updateObservers(); } } \ No newline at end of file diff --git a/src/forge/SpellAbility.java b/src/forge/SpellAbility.java index eaf33cfbc1f..4d63240befe 100644 --- a/src/forge/SpellAbility.java +++ b/src/forge/SpellAbility.java @@ -40,6 +40,7 @@ public abstract class SpellAbility { private Input afterPayMana; protected Ability_Cost payCosts = null; + protected Target chosenTarget = null; private Command cancelCommand = Command.Blank; private Command beforePayManaAI = Command.Blank; @@ -238,6 +239,14 @@ public abstract class SpellAbility { payCosts = abCost; } + public Target getTarget() { + return chosenTarget; + } + + public void setTarget(Target tgt) { + chosenTarget = tgt; + } + public Input getAfterResolve() { return afterResolve; } diff --git a/src/forge/SpellAbility_Requirements.java b/src/forge/SpellAbility_Requirements.java new file mode 100644 index 00000000000..111e346d04a --- /dev/null +++ b/src/forge/SpellAbility_Requirements.java @@ -0,0 +1,74 @@ +package forge; + +public class SpellAbility_Requirements { + private SpellAbility ability = null; + private Target_Selection select = null; + private Cost_Payment payment = null; + + private PlayerZone fromZone = null; + private boolean bCasting = false; + + public SpellAbility_Requirements(SpellAbility sa, Target_Selection ts, Cost_Payment cp){ + ability = sa; + select = ts; + payment = cp; + } + + public void fillRequirements(){ + if (ability instanceof Spell && !bCasting){ + // remove from hand, todo(sol) be careful of spell copies if spells start using this + bCasting = true; + Card c = ability.getSourceCard(); + fromZone = AllZone.getZone(c); + fromZone.remove(c); + } + + if (select.getTgt().doesTarget()){ + select.setRequirements(this); + select.chooseTargets(); + } + else + startPaying(); + } + + public void finishedTargeting(){ + if (select.isCanceled()){ + // cancel ability during target choosing + if (bCasting){ // and not a copy + // add back to hand + fromZone.add(ability.getSourceCard()); + } + + select.getTgt().resetTargets(); + return; + } + startPaying(); + } + + public void startPaying(){ + payment.setRequirements(this); + payment.payCost(); + } + + public void finishPaying(){ + if (payment.isAllPaid()) + addAbilityToStack(); + else if (payment.isCanceled()){ + if (bCasting){ // and not a copy + // add back to hand + fromZone.add(ability.getSourceCard()); + } + if (select.getTgt().doesTarget()) + select.getTgt().resetTargets(); + + payment.cancelPayment(); + } + } + + public void addAbilityToStack(){ + AllZone.ManaPool.clearPay(false); + AllZone.Stack.add(ability); + if (select != null) + select.getTgt().resetTargets(); + } +} diff --git a/src/forge/Target.java b/src/forge/Target.java new file mode 100644 index 00000000000..cda70a7142c --- /dev/null +++ b/src/forge/Target.java @@ -0,0 +1,60 @@ +package forge; + +public class Target { + private boolean tgtPlayer = false; + public boolean canTgtPlayer() { return tgtPlayer; } + private boolean tgtCreature = false; + public boolean canTgtCreature() { return tgtCreature; } + + public boolean canTgtCreaturePlayer() { return tgtCreature && tgtPlayer; } + public boolean doesTarget() { return tgtCreature || tgtPlayer; } + + private int minTargets = 0; + public int getMinTargets() { return minTargets; } + private int maxTargets = 0; + public int getMaxTargets() { return maxTargets; } + // add array of targets here? + + private int numTargeted = 0; + public int getNumTargeted() { return numTargeted; } + public void incrementTargets() { numTargeted++; } + public void resetTargets() { numTargeted = 0; } + + public Target(String parse){ + if (parse.contains("Tgt")){ + // Tgt{C}{P}[//] min-max is optional + String tgtStr = parse.replace("Tgt", ""); + String[] tgtSplit = tgtStr.split("/"); + + if (tgtSplit[0].contains("C")) // creature + tgtCreature = true; + if (tgtSplit[0].contains("P")) // player + tgtPlayer = true; + //todo add Opponent and other permanent types + + if (tgtSplit.length != 3){ + minTargets = 1; + maxTargets = 1; + } + else{ + minTargets = Integer.parseInt(tgtSplit[1]); + maxTargets = Integer.parseInt(tgtSplit[2]); + } + } + } + + public String targetString() + { + String tgt = ""; + if (tgtCreature) + tgt += "creature"; + if (tgtPlayer && !tgt.equals("")) + tgt += " or "; + if (tgtPlayer) + tgt += "player"; + + tgt += "."; + + return "target " + tgt; + } +} diff --git a/src/forge/Target_Selection.java b/src/forge/Target_Selection.java new file mode 100644 index 00000000000..17416f74515 --- /dev/null +++ b/src/forge/Target_Selection.java @@ -0,0 +1,182 @@ +package forge; + +public class Target_Selection { + private Target target = null; + private SpellAbility ability = null; + private Card card = null; + + public Target getTgt() { return target; } + public SpellAbility getAbility() { return ability; } + public Card getCard() { return card; } + + private SpellAbility_Requirements req = null; + public void setRequirements(SpellAbility_Requirements reqs) { req = reqs; } + + private boolean bCancel = false; + public void setCancel(boolean done) { bCancel = done; } + public boolean isCanceled() { return bCancel; } + private boolean bDoneTarget = false; + public void setDoneTarget(boolean done) { bDoneTarget = done; } + + final private Input changeInput = new Input() { + private static final long serialVersionUID = -5750122411788688459L; }; + + public Target_Selection(Target tgt, SpellAbility sa){ + target = tgt; + ability = sa; + card = sa.getSourceCard(); + } + + public boolean chooseTargets(){ + // if not enough targets chosen, reset and cancel Ability + if (bCancel || bDoneTarget && target.getNumTargeted() < target.getMinTargets()){ + bCancel = true; + target.resetTargets(); + return false; + } + + // if we haven't reached minimum targets, or we're stil less than Max targets keep choosing + // targetting, with forward code for multiple target abilities + if (!bDoneTarget && target.getMinTargets() > 0 && target.getNumTargeted() < target.getMaxTargets()){ + if (target.canTgtCreature() && target.canTgtPlayer()) + changeInput.stopSetNext(targetCreaturePlayer(ability, Command.Blank, true, this, req)); + else if(target.canTgtCreature()) + changeInput.stopSetNext(targetCreature(ability, this, req)); + else if(target.canTgtPlayer()) + changeInput.stopSetNext(targetPlayer(ability, this, req)); + return false; + } + + return true; + } + + public static Input targetCreaturePlayer(final SpellAbility ability, final Command paid, final boolean targeted, + final Target_Selection select, final SpellAbility_Requirements req) { + Input target = new Input() { + private static final long serialVersionUID = 2781418414287281005L; + + @Override + public void showMessage() { + AllZone.Display.showMessage("Select target Creature, Player, or Planeswalker"); + // when multi targets (Arc Mage) are added, need this: + // if payment.targeted < mintarget only enable cancel + // else if payment.targeted < maxtarget enable cancel and ok + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectButtonCancel() { + select.setCancel(true); + stop(); + req.finishedTargeting(); + } + + @Override + public void selectButtonOK() { + select.setDoneTarget(true); + stop(); + req.finishedTargeting(); + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + if((card.isCreature() || card.isPlaneswalker()) && zone.is(Constant.Zone.Play) + && (!targeted || CardFactoryUtil.canTarget(ability, card))) { + ability.setTargetCard(card); + done(); + } + }//selectCard() + + @Override + public void selectPlayer(String player) { + ability.setTargetPlayer(player); + // if multitarget increment then select again + done(); + } + + void done() { + select.getTgt().incrementTargets(); + paid.execute(); + stop(); + req.finishedTargeting(); + } + }; + return target; + }//input_targetCreaturePlayer() + + public static Input targetCreature(final SpellAbility ability, final Target_Selection select, final SpellAbility_Requirements req) { + Input target = new Input() { + private static final long serialVersionUID = 2781418414287281005L; + + @Override + public void showMessage() { + AllZone.Display.showMessage("Select target Creature"); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectButtonCancel() { + select.setCancel(true); + stop(); + req.finishedTargeting(); + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + if(card.isCreature() && zone.is(Constant.Zone.Play) && (CardFactoryUtil.canTarget(ability, card))) { + ability.setTargetCard(card); + done(); + } + }//selectCard() + + void done() { + select.getTgt().incrementTargets(); + stop(); + req.finishedTargeting(); + } + }; + return target; + }//targetCreature() + + public static Input targetPlayer(final SpellAbility ability, final Target_Selection select, final SpellAbility_Requirements req) { + Input target = new Input() { + private static final long serialVersionUID = 2781418414287281005L; + + @Override + public void showMessage() { + AllZone.Display.showMessage("Select target Player or Planeswalker"); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectButtonCancel() { + select.setCancel(true); + stop(); + req.finishedTargeting(); + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + if(card.isPlaneswalker() && zone.is(Constant.Zone.Play) && (!CardFactoryUtil.canTarget(ability, card))) { + ability.setTargetCard(card); + done(); + } + }//selectCard() + + @Override + public void selectPlayer(String player) { + ability.setTargetPlayer(player); + done(); + } + + void done() { + select.getTgt().incrementTargets(); + stop(); + req.finishedTargeting(); + } + }; + return target; + }//targetPlayer() + + +}