diff --git a/.gitattributes b/.gitattributes index 385876679af..4e97b1211f2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -634,6 +634,7 @@ res/cardsfolder/blazing_specter.txt -text svneol=native#text/plain res/cardsfolder/bleak_coven_vampires.txt -text svneol=native#text/plain res/cardsfolder/blessed_orator.txt -text svneol=native#text/plain res/cardsfolder/blessed_reversal.txt -text svneol=native#text/plain +res/cardsfolder/blessed_wind.txt svneol=native#text/plain res/cardsfolder/blessed_wine.txt -text svneol=native#text/plain res/cardsfolder/blessing.txt -text svneol=native#text/plain res/cardsfolder/blessing_of_leeches.txt svneol=native#text/plain @@ -3733,6 +3734,7 @@ res/cardsfolder/mageta_the_lion.txt -text svneol=native#text/plain res/cardsfolder/magetas_boon.txt -text svneol=native#text/plain res/cardsfolder/maggot_carrier.txt -text svneol=native#text/plain res/cardsfolder/maggot_therapy.txt -text svneol=native#text/plain +res/cardsfolder/magister_sphinx.txt -text svneol=native#text/plain res/cardsfolder/magistrates_scepter.txt -text svneol=native#text/plain res/cardsfolder/magistrates_veto.txt -text svneol=native#text/plain res/cardsfolder/magma_giant.txt -text svneol=native#text/plain diff --git a/res/cardsfolder/blessed_wind.txt b/res/cardsfolder/blessed_wind.txt new file mode 100644 index 00000000000..693533bca62 --- /dev/null +++ b/res/cardsfolder/blessed_wind.txt @@ -0,0 +1,8 @@ +Name:Blessed Wind +ManaCost:7 W W +Types:Sorcery +Text:no text +A:SP$SetLife | Cost$ 7 W W | ValidTgts$ Player | TgtPrompt$ Select target player | LifeAmount$ 20 | SpellDescription$ Target player's life total becomes 20. +SVar:Rarity:Rare +SVar:Picture:http://www.wizards.com/global/images/magic/general/blessed_wind.jpg +End \ No newline at end of file diff --git a/res/cardsfolder/magister_sphinx.txt b/res/cardsfolder/magister_sphinx.txt new file mode 100644 index 00000000000..6107634a464 --- /dev/null +++ b/res/cardsfolder/magister_sphinx.txt @@ -0,0 +1,11 @@ +Name:Magister Sphinx +ManaCost:4 W U B +Types:Artifact Creature Sphinx +Text:no text +PT:5/5 +K:Flying +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigLife | TriggerDescription$ When CARDNAME enters the battlefield, target player's life total becomes 10. +SVar:TrigLife:AB$SetLife | Cost$ 0 | ValidTgts$ Player | TgtPrompt$ Select target player | LifeAmount$ 10 +SVar:Rarity:Rare +SVar:Picture:http://www.wizards.com/global/images/magic/general/magister_sphinx.jpg +End \ No newline at end of file diff --git a/src/forge/AbilityFactory.java b/src/forge/AbilityFactory.java index 7ac4b6a9285..4d3382c51d1 100644 --- a/src/forge/AbilityFactory.java +++ b/src/forge/AbilityFactory.java @@ -339,6 +339,15 @@ public class AbilityFactory { SA = AbilityFactory_AlterLife.createDrawbackLoseLife(this); } + if (API.equals("SetLife")){ + if (isAb) + SA = AbilityFactory_AlterLife.createAbilitySetLife(this); + else if (isSp) + SA = AbilityFactory_AlterLife.createSpellSetLife(this); + else if (isDb) + SA = AbilityFactory_AlterLife.createDrawbackSetLife(this); + } + if (API.equals("Poison")){ if (isAb) SA = AbilityFactory_AlterLife.createAbilityPoison(this); diff --git a/src/forge/AbilityFactory_AlterLife.java b/src/forge/AbilityFactory_AlterLife.java index 389c16684bd..f52e7b1fad6 100644 --- a/src/forge/AbilityFactory_AlterLife.java +++ b/src/forge/AbilityFactory_AlterLife.java @@ -742,4 +742,255 @@ public class AbilityFactory_AlterLife { return true; } -} + + // ************************************************************************* + // ************************** SET LIFE ************************************* + // ************************************************************************* + + public static SpellAbility createAbilitySetLife(final AbilityFactory AF) { + final SpellAbility abSetLife = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { + private static final long serialVersionUID = -7375434097541097668L; + final AbilityFactory af = AF; + + @Override + public String getStackDescription() { + return setLifeStackDescription(af, this); + } + + public boolean canPlayAI() { + return setLifeCanPlayAI(af, this); + } + + @Override + public void resolve() { + setLifeResolve(af, this); + } + + @Override + public boolean doTrigger(boolean mandatory) { + return setLifeDoTriggerAI(af, this, mandatory); + } + + }; + return abSetLife; + } + + public static SpellAbility createSpellSetLife(final AbilityFactory AF) { + final SpellAbility spSetLife = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { + private static final long serialVersionUID = -94657822256270222L; + final AbilityFactory af = AF; + + @Override + public String getStackDescription() { + return setLifeStackDescription(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 setLifeCanPlayAI(af, this); + } + + @Override + public void resolve() { + setLifeResolve(af, this); + } + + }; + return spSetLife; + } + + public static SpellAbility createDrawbackSetLife(final AbilityFactory AF) { + final SpellAbility dbSetLife = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) { + private static final long serialVersionUID = -7634729949893534023L; + final AbilityFactory af = AF; + + @Override + public String getStackDescription() { + return setLifeStackDescription(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 setLifeCanPlayAI(af, this); + } + + @Override + public void resolve() { + setLifeResolve(af, this); + } + + @Override + public boolean chkAI_Drawback() { + return true; + } + + @Override + public boolean doTrigger(boolean mandatory) { + return setLifeDoTriggerAI(af, this, mandatory); + } + + }; + return dbSetLife; + } + + private static String setLifeStackDescription(AbilityFactory af, SpellAbility sa) { + StringBuilder sb = new StringBuilder(); + int amount = AbilityFactory.calculateAmount(af.getHostCard(), af.getMapParams().get("LifeAmount"), sa); + + if (!(sa instanceof Ability_Sub)) + sb.append(sa.getSourceCard().getName()).append(" -"); + + sb.append(" "); + + ArrayList tgtPlayers; + + Target tgt = af.getAbTgt(); + if(tgt != null) + tgtPlayers = tgt.getTargetPlayers(); + else + tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), af.getMapParams().get("Defined"), sa); + + for(Player player : tgtPlayers) + sb.append(player).append(" "); + + sb.append("life total becomes ").append(amount).append("."); + + Ability_Sub abSub = sa.getSubAbility(); + if(abSub != null) { + sb.append(abSub.getStackDescription()); + } + + return sb.toString(); + } + + private static boolean setLifeCanPlayAI(final AbilityFactory af, final SpellAbility sa) { + Random r = new Random(); + //Ability_Cost abCost = sa.getPayCosts(); + final Card source = sa.getSourceCard(); + int life = AllZone.ComputerPlayer.getLife(); + int hlife = AllZone.HumanPlayer.getLife(); + String amountStr = af.getMapParams().get("LifeAmount"); + + if(!ComputerUtil.canPayCost(sa)) + return false; + + if(!AllZone.ComputerPlayer.canGainLife()) + return false; + + // TODO handle proper calculation of X values based on Cost and what would be paid + int amount; + //we shouldn't have to worry too much about PayX for SetLife + if(amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")) { + // Set PayX here to maximum value. + int xPay = ComputerUtil.determineLeftoverMana(sa); + source.setSVar("PayX", Integer.toString(xPay)); + amount = xPay; + } + else + amount = AbilityFactory.calculateAmount(af.getHostCard(), amountStr, sa); + + // prevent run-away activations - first time will always return true + boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed()); + + Target tgt = sa.getTarget(); + if(tgt != null){ + tgt.resetTargets(); + if(tgt.canOnlyTgtOpponent()) { + tgt.addTarget(AllZone.HumanPlayer); + //if we can only target the human, and the Human's life would go up, don't play it. + //possibly add a combo here for Magister Sphinx and Higedetsu's (sp?) Second Rite + if(amount > hlife || !AllZone.HumanPlayer.canLoseLife()) return false; + } + else { + if(amount > life && life <= 10) tgt.addTarget(AllZone.ComputerPlayer); + else if(hlife > amount) tgt.addTarget(AllZone.HumanPlayer); + else if(amount > life) tgt.addTarget(AllZone.ComputerPlayer); + else return false; + } + } + else { + if(amount < life) return false; + } + + //if life is in danger, always activate + if(life < 3 && amount > life) return true; + + return ((r.nextFloat() < .6667) && chance); + } + + private static boolean setLifeDoTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory){ + int life = AllZone.ComputerPlayer.getLife(); + int hlife = AllZone.HumanPlayer.getLife(); + Card source = sa.getSourceCard(); + String amountStr = af.getMapParams().get("LifeAmount"); + if (!ComputerUtil.canPayCost(sa) && !mandatory) // If there is a cost payment it's usually not mandatory + return false; + + int amount; + if (amountStr.equals("X") && source.getSVar(amountStr).equals("Count$xPaid")){ + // Set PayX here to maximum value. + int xPay = ComputerUtil.determineLeftoverMana(sa); + source.setSVar("PayX", Integer.toString(xPay)); + amount = xPay; + } + else + amount = AbilityFactory.calculateAmount(af.getHostCard(), amountStr, sa); + + // If the Target is gaining life, target self. + // if the Target is modifying how much life is gained, this needs to be handled better + Target tgt = sa.getTarget(); + if (tgt != null){ + tgt.resetTargets(); + if (tgt.canOnlyTgtOpponent()) + tgt.addTarget(AllZone.HumanPlayer); + else { + if(amount > life && life <= 10) tgt.addTarget(AllZone.ComputerPlayer); + else if(hlife > amount) tgt.addTarget(AllZone.HumanPlayer); + else if(amount > life) tgt.addTarget(AllZone.ComputerPlayer); + else return false; + } + } + + // check SubAbilities DoTrigger? + Ability_Sub abSub = sa.getSubAbility(); + if (abSub != null) { + return abSub.doTrigger(mandatory); + } + + return true; + } + + private static void setLifeResolve(final AbilityFactory af, final SpellAbility sa) { + HashMap params = af.getMapParams(); + Card card = af.getHostCard(); + int lifeAmount = AbilityFactory.calculateAmount(af.getHostCard(), params.get("LifeAmount"), sa); + ArrayList tgtPlayers; + + Target tgt = af.getAbTgt(); + if(tgt != null && !params.containsKey("Defined")) + tgtPlayers = tgt.getTargetPlayers(); + else + tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); + + for(Player p : tgtPlayers) + if(tgt == null || p.canTarget(af.getHostCard())) + p.setLife(lifeAmount, sa.getSourceCard()); + + if(af.hasSubAbility()) { + Ability_Sub abSub = sa.getSubAbility(); + if(abSub != null) { + 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); + } + } + } + +}//end class AbilityFactory_AlterLife