From 0c403d208237e578e2034aa155844e82e53ce23a Mon Sep 17 00:00:00 2001 From: swordshine Date: Mon, 10 Jun 2013 03:54:54 +0000 Subject: [PATCH] - Added Research // Development, Tainted Specter, and Uba Mask --- .gitattributes | 3 + res/cardsfolder/p/psychic_vortex.txt | 2 +- res/cardsfolder/r/research_development.txt | 18 +++ res/cardsfolder/t/tainted_specter.txt | 10 ++ res/cardsfolder/u/uba_mask.txt | 13 +++ src/main/java/forge/card/cost/Cost.java | 6 +- src/main/java/forge/card/cost/CostDraw.java | 18 ++- .../card/cost/CostExiledMoveToGrave.java | 2 +- .../forge/card/cost/CostPutCardToLib.java | 9 ++ src/main/java/forge/game/ai/ComputerUtil.java | 4 +- .../java/forge/game/ai/ComputerUtilCost.java | 10 +- .../java/forge/game/player/HumanPlay.java | 105 ++++++++++++------ 12 files changed, 143 insertions(+), 57 deletions(-) create mode 100644 res/cardsfolder/r/research_development.txt create mode 100644 res/cardsfolder/t/tainted_specter.txt create mode 100644 res/cardsfolder/u/uba_mask.txt diff --git a/.gitattributes b/.gitattributes index 29c3ebee56a..dc5983e83d4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8781,6 +8781,7 @@ res/cardsfolder/r/requiem_angel.txt -text res/cardsfolder/r/reroute.txt -text res/cardsfolder/r/rescind.txt svneol=native#text/plain res/cardsfolder/r/rescue.txt svneol=native#text/plain +res/cardsfolder/r/research_development.txt -text res/cardsfolder/r/research_the_deep.txt svneol=native#text/plain res/cardsfolder/r/reset.txt svneol=native#text/plain res/cardsfolder/r/reshape.txt svneol=native#text/plain @@ -10939,6 +10940,7 @@ res/cardsfolder/t/tainted_isle.txt svneol=native#text/plain res/cardsfolder/t/tainted_pact.txt -text res/cardsfolder/t/tainted_peak.txt svneol=native#text/plain res/cardsfolder/t/tainted_sigil.txt -text +res/cardsfolder/t/tainted_specter.txt -text res/cardsfolder/t/tainted_strike.txt svneol=native#text/plain res/cardsfolder/t/tainted_well.txt svneol=native#text/plain res/cardsfolder/t/tainted_wood.txt svneol=native#text/plain @@ -11678,6 +11680,7 @@ res/cardsfolder/t/typhoon.txt svneol=native#text/plain res/cardsfolder/t/tyrannize.txt -text res/cardsfolder/t/tyrant_of_discord.txt -text res/cardsfolder/t/tyrranax.txt svneol=native#text/plain +res/cardsfolder/u/uba_mask.txt -text res/cardsfolder/u/ubul_sar_gatekeepers.txt -text res/cardsfolder/u/uktabi_drake.txt svneol=native#text/plain res/cardsfolder/u/uktabi_efreet.txt svneol=native#text/plain diff --git a/res/cardsfolder/p/psychic_vortex.txt b/res/cardsfolder/p/psychic_vortex.txt index 1ee8ba6fe5d..ee44541a569 100644 --- a/res/cardsfolder/p/psychic_vortex.txt +++ b/res/cardsfolder/p/psychic_vortex.txt @@ -1,7 +1,7 @@ Name:Psychic Vortex ManaCost:2 U U Types:Enchantment -K:Cumulative upkeep:DrawYou<1>:Draw a card. +K:Cumulative upkeep:Draw<1/You>:Draw a card. T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | Execute$ TrigSac | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your end step, sacrifice a land and discard your hand. SVar:TrigSac:AB$ Sacrifice | Cost$ 0 | SacValid$ Land | SubAbility$ DBDiscard SVar:DBDiscard:DB$ Discard | Mode$ Hand diff --git a/res/cardsfolder/r/research_development.txt b/res/cardsfolder/r/research_development.txt new file mode 100644 index 00000000000..b6fcbb2f5d4 --- /dev/null +++ b/res/cardsfolder/r/research_development.txt @@ -0,0 +1,18 @@ +Name:Research +ManaCost:G U +AlternateMode: Split +Types:Instant +A:SP$ ChangeZone | Cost$ U R | Origin$ Sideboard | Destination$ Library | Shuffle$ True | ChangeType$ Card.YouOwn | ChangeNum$ 4 | SpellDescription$ Choose up to four cards you own from outside the game and shuffle them into your library. +SVar:RemRandomDeck:True +SVar:Picture:http://www.wizards.com/global/images/magic/general/research_development.jpg +Oracle:Choose up to four cards you own from outside the game and shuffle them into your library. +ALTERNATE +Name:Development +ManaCost:3 U R +Types:Instant +A:SP$ Repeat | Cost$ 3 U R | RepeatSubAbility$ DBToken | MaxRepeat$ 3 | StackDescription$ SpellDescription | SubAbility$ DBCleanup | SpellDescription$ Put a 3/1 red Elemental creature token onto the battlefield unless any opponent has you draw a card. Repeat this process two more times. +SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenName$ Elemental | TokenTypes$ Creature,Elemental | TokenOwner$ You | TokenColors$ Red | TokenPower$ 3 | TokenToughness$ 1 | TokenKeywords$ Haste | UnlessPayer$ Player.Opponent | UnlessCost$ Draw<1/Player.IsRemembered> | UnlessAI$ MorePowerful +T:Mode$ SpellCast | ValidCard$ Card.Self | Execute$ TrigRemember | Static$ True +SVar:TrigRemember:DB$ Pump | RememberObjects$ TriggeredActivator +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +Oracle:Put a 3/1 red Elemental creature token onto the battlefield unless any opponent has you draw a card. Repeat this process two more times. diff --git a/res/cardsfolder/t/tainted_specter.txt b/res/cardsfolder/t/tainted_specter.txt new file mode 100644 index 00000000000..c35a5d8944f --- /dev/null +++ b/res/cardsfolder/t/tainted_specter.txt @@ -0,0 +1,10 @@ +Name:Tainted Specter +ManaCost:3 B +Types:Creature Specter +PT:2/2 +K:Flying +A:AB$ Discard | Cost$ 1 B B T | ValidTgts$ Player | SorcerySpeed$ True | NumCards$ 1 | Mode$ TgtChoose | SorcerySpeed$ True | RememberDiscarded$ True | SubAbility$ DBDmg | UnlessPayer$ Targeted | UnlessCost$ PutCardToLibFromHand<1/0/Card> | SpellDescription$ Target player discards a card unless he or she puts a card from his or her hand on top of his or her library. If that player discards a card this way, CARDNAME deals 1 damage to each creature and each player. Activate this ability only any time you could cast a sorcery. +SVar:DBDmg:DB$ DamageAll | ValidCards$ Creature | ValidPlayers$ Each | NumDmg$ 1 | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE1 | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:Picture:http://www.wizards.com/global/images/magic/general/tainted_specter.jpg +Oracle:Flying\n{1}{B}{B}, {T}: Target player discards a card unless he or she puts a card from his or her hand on top of his or her library. If that player discards a card this way, Tainted Specter deals 1 damage to each creature and each player. Activate this ability only any time you could cast a sorcery. diff --git a/res/cardsfolder/u/uba_mask.txt b/res/cardsfolder/u/uba_mask.txt new file mode 100644 index 00000000000..60ba332aa9c --- /dev/null +++ b/res/cardsfolder/u/uba_mask.txt @@ -0,0 +1,13 @@ +Name:Uba Mask +ManaCost:4 +Types:Artifact +R:Event$ Draw | ActiveZones$ Battlefield | ValidPlayer$ Player | ReplaceWith$ ExileTop | Description$ If a player would draw a card, that player exiles that card face up instead. +SVar:ExileTop:AB$ Mill | Cost$ 0 | Defined$ ReplacedPlayer | NumCards$ 1 | Destination$ Exile | RememberMilled$ True +S:Mode$ Continuous | AffectedZone$ Exile | Affected$ Card.IsRemembered | AddHiddenKeyword$ May be played | Description$ Each player may play cards he or she exiled with CARDNAME this turn. +T:Mode$ Phase | Phase$ Cleanup | Execute$ TrigReset | Static$ True +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReset | Static$ True +SVar:TrigReset:DB$ Cleanup | ClearRemembered$ True +T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered | Execute$ DBForget +SVar:DBForget:DB$ Pump | Defined$ TriggeredCard | ForgetObjects$ TriggeredCard +SVar:Picture:http://www.wizards.com/global/images/magic/general/uba_mask.jpg +Oracle:If a player would draw a card, that player exiles that card face up instead.\nEach player may play cards he or she exiled with Uba Mask this turn. diff --git a/src/main/java/forge/card/cost/Cost.java b/src/main/java/forge/card/cost/Cost.java index 1931017d091..2a9859c17de 100644 --- a/src/main/java/forge/card/cost/Cost.java +++ b/src/main/java/forge/card/cost/Cost.java @@ -320,9 +320,9 @@ public class Cost { return new CostExiledMoveToGrave(splitStr[0], splitStr[1], description); } - if (parse.startsWith("DrawYou<")) { - final String[] splitStr = abCostParse(parse, 1); - return new CostDraw(splitStr[0], "You"); + if (parse.startsWith("Draw<")) { + final String[] splitStr = abCostParse(parse, 2); + return new CostDraw(splitStr[0], splitStr[1]); } if (parse.startsWith("PutCardToLibFromHand<")) { diff --git a/src/main/java/forge/card/cost/CostDraw.java b/src/main/java/forge/card/cost/CostDraw.java index c9e5cc65e0f..00f47b54beb 100644 --- a/src/main/java/forge/card/cost/CostDraw.java +++ b/src/main/java/forge/card/cost/CostDraw.java @@ -58,16 +58,12 @@ public class CostDraw extends CostPart { * @param payer * @param source */ - private List getPotentialPlayers(final Player payer, final SpellAbility ability) { + private List getPotentialPlayers(final Player payer, final Card source) { List res = new ArrayList(); String type = this.getType(); - if ("Activator".equals(type)) { - res.add(ability.getActivatingPlayer()); - } else { - for (Player p : payer.getGame().getPlayers()) { - if (p.isValid(type, payer, ability.getSourceCard())) { - res.add(p); - } + for (Player p : payer.getGame().getPlayers()) { + if (p.isValid(type, payer, source) && p.canDraw()) { + res.add(p); } } return res; @@ -82,7 +78,7 @@ public class CostDraw extends CostPart { */ @Override public final boolean canPay(final SpellAbility ability) { - List potentials = getPotentialPlayers(ability.getActivatingPlayer(), ability); + List potentials = getPotentialPlayers(ability.getActivatingPlayer(), ability.getSourceCard()); return !potentials.isEmpty(); } @@ -94,7 +90,7 @@ public class CostDraw extends CostPart { */ @Override public final void payAI(final PaymentDecision decision, final Player ai, SpellAbility ability, Card source) { - for (final Player p : getPotentialPlayers(ai, ability)) { + for (final Player p : getPotentialPlayers(ai, ability.getSourceCard())) { p.drawCards(decision.c); } } @@ -109,7 +105,7 @@ public class CostDraw extends CostPart { @Override public final boolean payHuman(final SpellAbility ability, final Game game) { final String amount = this.getAmount(); - final List players = getPotentialPlayers(ability.getActivatingPlayer(), ability); + final List players = getPotentialPlayers(ability.getActivatingPlayer(), ability.getSourceCard()); final Card source = ability.getSourceCard(); Integer c = this.convertAmount(); diff --git a/src/main/java/forge/card/cost/CostExiledMoveToGrave.java b/src/main/java/forge/card/cost/CostExiledMoveToGrave.java index 867bdfbb82b..5950df71427 100644 --- a/src/main/java/forge/card/cost/CostExiledMoveToGrave.java +++ b/src/main/java/forge/card/cost/CostExiledMoveToGrave.java @@ -170,7 +170,7 @@ public class CostExiledMoveToGrave extends CostPartWithList { List typeList = ai.getGame().getCardsIn(ZoneType.Exile); - typeList = CardLists.getValidCards(typeList, this.getType().split(","), ai, source); + typeList = CardLists.getValidCards(typeList, this.getType().split(";"), ai, source); if (typeList.size() < c) { return null; diff --git a/src/main/java/forge/card/cost/CostPutCardToLib.java b/src/main/java/forge/card/cost/CostPutCardToLib.java index 38186f27a3b..9d4bd2a26a4 100644 --- a/src/main/java/forge/card/cost/CostPutCardToLib.java +++ b/src/main/java/forge/card/cost/CostPutCardToLib.java @@ -64,6 +64,15 @@ public class CostPutCardToLib extends CostPartWithList { return this.libPosition; } + /** + * isSameZone. + * + * @return a boolean + */ + public final boolean isSameZone() { + return this.sameZone; + } + /** * Instantiates a new cost CostPutCardToLib. * diff --git a/src/main/java/forge/game/ai/ComputerUtil.java b/src/main/java/forge/game/ai/ComputerUtil.java index 969ba64235a..7e445968ed5 100644 --- a/src/main/java/forge/game/ai/ComputerUtil.java +++ b/src/main/java/forge/game/ai/ComputerUtil.java @@ -487,7 +487,7 @@ public class ComputerUtil { } else { typeList = ai.getCardsIn(zone); } - typeList = CardLists.getValidCards(typeList, type.split(","), activate.getController(), activate); + typeList = CardLists.getValidCards(typeList, type.split(";"), activate.getController(), activate); if ((target != null) && target.getController() == ai && typeList.contains(target)) { typeList.remove(target); // don't exile the card we're pumping @@ -527,7 +527,7 @@ public class ComputerUtil { final Card target, final int amount) { List typeList = ai.getCardsIn(zone); - typeList = CardLists.getValidCards(typeList, type.split(","), activate.getController(), activate); + typeList = CardLists.getValidCards(typeList, type.split(";"), activate.getController(), activate); if ((target != null) && target.getController() == ai && typeList.contains(target)) { typeList.remove(target); // don't move the card we're pumping diff --git a/src/main/java/forge/game/ai/ComputerUtilCost.java b/src/main/java/forge/game/ai/ComputerUtilCost.java index 33e87b4b83f..cb4e35d1747 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCost.java +++ b/src/main/java/forge/game/ai/ComputerUtilCost.java @@ -408,13 +408,19 @@ public class ComputerUtilCost { if (c == null || c.isUntapped()) { return false; } + } else if ("MorePowerful".equals(sa.getParam("UnlessAI"))) { + final int sourceCreatures = sa.getActivatingPlayer().getCreaturesInPlay().size(); + final int payerCreatures = payer.getCreaturesInPlay().size(); + if (payerCreatures > sourceCreatures + 1) { + return false; + } } - + // AI will only pay when it's not already payed and only opponents abilities if (alreadyPaid || (payers.size() > 1 && (isMine && !payForOwnOnly))) { return false; } - + // AI was crashing because the blank ability used to pay costs // Didn't have any of the data on the original SA to pay dependant costs diff --git a/src/main/java/forge/game/player/HumanPlay.java b/src/main/java/forge/game/player/HumanPlay.java index 2853f608a77..3264d52f705 100644 --- a/src/main/java/forge/game/player/HumanPlay.java +++ b/src/main/java/forge/game/player/HumanPlay.java @@ -305,7 +305,7 @@ public class HumanPlay { if (parts.isEmpty() || costPart.getAmount().equals("0")) { return GuiDialog.confirm(source, "Do you want to pay 0?" + orString); } - + //the following costs do not need inputs for (CostPart part : parts) { boolean mayRemovePart = true; @@ -314,24 +314,41 @@ public class HumanPlay { final int amount = getAmountFromPart(part, source, sourceAbility); if (!p.canPayLife(amount)) return false; - + if (false == GuiDialog.confirm(source, "Do you want to pay " + amount + " life?" + orString)) return false; - + p.payLife(amount, null); } else if (part instanceof CostDraw) { final int amount = getAmountFromPart(part, source, sourceAbility); - if (!p.canDraw()) { + List res = new ArrayList(); + String type = part.getType(); + for (Player player : p.getGame().getPlayers()) { + if (player.isValid(type, p, source) && player.canDraw()) { + res.add(player); + } + } + + if (res.isEmpty()) { return false; } - if (false == GuiDialog.confirm(source, "Do you want to draw " + amount + " card(s)?" + orString)) { + StringBuilder sb = new StringBuilder(); + + sb.append("Do you want to "); + sb.append(res.contains(p) ? "" : "let that player "); + sb.append(amount); + sb.append(" card(s)?" + orString); + + if (!GuiDialog.confirm(source, sb.toString())) { return false; } - p.drawCards(amount); + for (Player player : res) { + player.drawCards(amount); + } } else if (part instanceof CostMill) { @@ -412,47 +429,60 @@ public class HumanPlay { } } } - + else if (part instanceof CostPutCardToLib) { - //Jotun Grunt int amount = Integer.parseInt(((CostPutCardToLib) part).getAmount()); final ZoneType from = ((CostPutCardToLib) part).getFrom(); - List list = CardLists.getValidCards(p.getGame().getCardsIn(from), part.getType().split(";"), p, source); - List players = p.getGame().getPlayers(); - List payableZone = new ArrayList(); - for (Player player : players) { - List enoughType = CardLists.filter(list, CardPredicates.isOwner(player)); - if (enoughType.size() < amount) { - list.removeAll(enoughType); - } else { - payableZone.add(player); - } - } - Player chosen = GuiChoose.oneOrNone(String.format("Put cards from whose %s?", - ((CostPutCardToLib) part).getFrom()), payableZone); - if (chosen == null) { - return false; + final boolean sameZone = ((CostPutCardToLib) part).isSameZone(); + List list; + if (sameZone) { + list = p.getGame().getCardsIn(from); + } else { + list = p.getCardsIn(from); } - - List typeList = CardLists.filter(list, CardPredicates.isOwner(chosen)); - - for (int i = 0; i < amount; i++) { - if (typeList.isEmpty()) { + list = CardLists.getValidCards(list, part.getType().split(";"), p, source); + + if (sameZone) { // Jotun Grunt + List players = p.getGame().getPlayers(); + List payableZone = new ArrayList(); + for (Player player : players) { + List enoughType = CardLists.filter(list, CardPredicates.isOwner(player)); + if (enoughType.size() < amount) { + list.removeAll(enoughType); + } else { + payableZone.add(player); + } + } + Player chosen = GuiChoose.oneOrNone(String.format("Put cards from whose %s?", from), payableZone); + if (chosen == null) { return false; } - - final Card c = GuiChoose.oneOrNone("Put cards to Library", typeList); - - if (c != null) { - typeList.remove(c); - p.getGame().getAction().moveToLibrary(c, Integer.parseInt(((CostPutCardToLib) part).getLibPos())); - } else { + + List typeList = CardLists.filter(list, CardPredicates.isOwner(chosen)); + + for (int i = 0; i < amount; i++) { + if (typeList.isEmpty()) { + return false; + } + + final Card c = GuiChoose.oneOrNone("Put cards to Library", typeList); + + if (c != null) { + typeList.remove(c); + p.getGame().getAction().moveToLibrary(c, Integer.parseInt(((CostPutCardToLib) part).getLibPos())); + } else { + return false; + } + } + } else if (from == ZoneType.Hand) { // Tainted Specter + boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "put into library." + orString); + if (!hasPaid) { return false; } } return true; } - + else if (part instanceof CostSacrifice) { int amount = Integer.parseInt(((CostSacrifice)part).getAmount()); List list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source); @@ -484,8 +514,9 @@ public class HumanPlay { else if (part instanceof CostPartMana ) { if (!((CostPartMana) part).getManaToPay().isZero()) // non-zero costs require input mayRemovePart = false; - } else + } else { throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - An unhandled type of cost was met: " + part.getClass()); + } if( mayRemovePart ) remainingParts.remove(part);