diff --git a/.gitattributes b/.gitattributes index 90286c134c3..3e5bcae786c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -716,6 +716,7 @@ forge-game/src/main/java/forge/game/trigger/TriggerDevoured.java -text forge-game/src/main/java/forge/game/trigger/TriggerDiscarded.java svneol=native#text/plain forge-game/src/main/java/forge/game/trigger/TriggerDrawn.java svneol=native#text/plain forge-game/src/main/java/forge/game/trigger/TriggerEvolved.java -text +forge-game/src/main/java/forge/game/trigger/TriggerExerted.java -text forge-game/src/main/java/forge/game/trigger/TriggerExploited.java -text forge-game/src/main/java/forge/game/trigger/TriggerFight.java -text forge-game/src/main/java/forge/game/trigger/TriggerFlippedCoin.java -text @@ -16855,6 +16856,7 @@ forge-gui/res/cardsfolder/upcoming/trueheart_duelist.txt -text forge-gui/res/cardsfolder/upcoming/unwavering_initiate.txt -text forge-gui/res/cardsfolder/upcoming/vizier_of_remedies.txt -text forge-gui/res/cardsfolder/upcoming/vizier_of_tumbling_sands.txt -text +forge-gui/res/cardsfolder/upcoming/watchful_naga.txt -text forge-gui/res/cardsfolder/upcoming/wayward_servant.txt -text forge-gui/res/cardsfolder/upcoming/winds_of_rebuke.txt -text forge-gui/res/cardsfolder/upcoming/winged_shepherd.txt -text diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index ca81283b89c..1fc6af3d705 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -30,11 +30,7 @@ import forge.card.CardTypeView; import forge.game.GameEntity; import forge.game.ability.ApiType; import forge.game.ability.effects.ProtectEffect; -import forge.game.card.Card; -import forge.game.card.CardCollectionView; -import forge.game.card.CardLists; -import forge.game.card.CardPredicates; -import forge.game.card.CounterType; +import forge.game.card.*; import forge.game.combat.Combat; import forge.game.combat.CombatUtil; import forge.game.combat.GlobalAttackRestrictions; @@ -78,11 +74,7 @@ public class AiAttackController { *

* Constructor for ComputerUtil_Attack2. *

- * - * @param possibleAttackers - * a {@link forge.CardList} object. - * @param possibleBlockers - * a {@link forge.CardList} object. + * */ public AiAttackController(final Player ai) { this.ai = ai; @@ -156,10 +148,7 @@ public class AiAttackController { *

* sortAttackers. *

- * - * @param in - * a {@link forge.CardList} object. - * @return a {@link forge.CardList} object. + * */ public final static List sortAttackers(final List in) { final List list = new ArrayList(); @@ -971,7 +960,7 @@ public class AiAttackController { * @param attacker * a {@link forge.game.card.Card} object. * @param defenders - * a {@link forge.CardList} object. + * a object. * @param combat * a {@link forge.game.combat.Combat} object. * @return a boolean. @@ -1117,6 +1106,18 @@ public class AiAttackController { } return false; // don't attack } + + public static List exertAttackers(List attackers) { + List exerters = Lists.newArrayList(); + for(Card c : attackers) { + // TODO Improve when the AI wants to use Exert powers + if (random.nextBoolean()) { + exerters.add(c); + } + } + + return exerters; + } /** * Find a protection type that will make an attacker unblockable. @@ -1181,5 +1182,4 @@ public class AiAttackController { } return null; //should never get here } - } // end class ComputerUtil_Attack2 diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 3901cc25bfc..8b42701348c 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -231,11 +231,16 @@ public class PlayerControllerAi extends PlayerController { public CardCollection orderBlockers(Card attacker, CardCollection blockers) { return AiBlockController.orderBlockers(attacker, blockers); } - + + @Override + public List exertAttackers(List attackers) { + return AiAttackController.exertAttackers(attackers); + } + @Override public CardCollection orderBlocker(Card attacker, Card blocker, CardCollection oldBlockers) { return AiBlockController.orderBlocker(attacker, blocker, oldBlockers); - }; + } @Override public CardCollection orderAttackers(Card blocker, CardCollection attackers) { diff --git a/forge-game/src/main/java/forge/game/keyword/Keyword.java b/forge-game/src/main/java/forge/game/keyword/Keyword.java index 6c65c9fc5de..cdd5065bf53 100644 --- a/forge-game/src/main/java/forge/game/keyword/Keyword.java +++ b/forge-game/src/main/java/forge/game/keyword/Keyword.java @@ -49,6 +49,7 @@ public enum Keyword { EVOKE(KeywordWithCost.class, false, "You may cast this spell for its evoke cost. If you do, it's sacrificed when it enters the battlefield."), EVOLVE(SimpleKeyword.class, false, "Whenever a creature enters the battlefield under your control, if that creature has greater power or toughness than this creature, put a +1/+1 counter on this creature."), EXALTED(SimpleKeyword.class, false, "Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn."), + EXERTED(SimpleKeyword.class, true, "This creature won't untap during your next untap step."), EXPLOIT(SimpleKeyword.class, false, "When this creature enters the battlefield, you may sacrifice a creature."), EXTORT(SimpleKeyword.class, false, "Whenever you cast a spell, you may pay {W/B}. If you do, each opponent loses 1 life and you gain that much life."), FABRICATE(KeywordWithAmount.class, false, "When this creature enters the battlefield, put {%1$d:+1/+1 counter} on it, or create {%1$d:1/1 colorless Servo artifact creature token}."), diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index dc311796bab..e4d28a89b92 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -508,6 +508,18 @@ public class PhaseHandler implements java.io.Serializable { } } while (!success); + + // Exert creatures here + List possibleExerters = CardLists.getKeyword(combat.getAttackers(), + "You may exert CARDNAME as it attacks."); + + for(Card exerter : whoDeclares.getController().exertAttackers(possibleExerters)) { + exerter.addExtrinsicKeyword("Exerted"); + final HashMap runParams = new HashMap(); + runParams.put("Card", exerter); + runParams.put("Player", playerTurn); + game.getTriggerHandler().runTrigger(TriggerType.Exerted, runParams, false); + } } if (game.isGameOver()) { // they just like to close window at any moment diff --git a/forge-game/src/main/java/forge/game/phase/Untap.java b/forge-game/src/main/java/forge/game/phase/Untap.java index 71906746e03..aa2ad7384af 100644 --- a/forge-game/src/main/java/forge/game/phase/Untap.java +++ b/forge-game/src/main/java/forge/game/phase/Untap.java @@ -17,17 +17,8 @@ */ package forge.game.phase; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - import com.google.common.base.Predicate; import com.google.common.base.Predicates; - import forge.card.CardType; import forge.game.Game; import forge.game.GameEntity; @@ -43,6 +34,9 @@ import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.util.collect.FCollection; +import java.util.*; +import java.util.Map.Entry; + /** *

* Untap class. @@ -91,7 +85,8 @@ public class Untap extends Phase { if (c.hasKeyword("CARDNAME doesn't untap during your untap step.") || c.hasKeyword("This card doesn't untap during your next untap step.") || c.hasKeyword("This card doesn't untap during your next two untap steps.") - || c.hasKeyword("This card doesn't untap.")) { + || c.hasKeyword("This card doesn't untap.") + || c.hasKeyword("Exerted")) { return false; } return true; @@ -211,6 +206,7 @@ public class Untap extends Phase { c.removeAllExtrinsicKeyword("HIDDEN This card doesn't untap during your next two untap steps."); c.addHiddenExtrinsicKeyword("HIDDEN This card doesn't untap during your next untap step."); } + c.removeAllExtrinsicKeyword("Exerted"); } } // end doUntap diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index 352a7cde0be..eeb205432fb 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -57,7 +57,7 @@ public abstract class PlayerController { Echo, Multikicker, Replicate, - CumulativeUpkeep; + CumulativeUpkeep, } public static enum BinaryChoiceType { @@ -126,6 +126,7 @@ public abstract class PlayerController { public abstract Player chooseStartingPlayer(boolean isFirstGame); public abstract CardCollection orderBlockers(Card attacker, CardCollection blockers); + public abstract List exertAttackers(List attackers); /** * Add a card to a pre-existing blocking order. * @param attacker the attacking creature. @@ -179,7 +180,7 @@ public abstract class PlayerController { public abstract int chooseNumber(SpellAbility sa, String title, List values, Player relatedPlayer); public int chooseNumber(SpellAbility sa, String string, int min, int max, Map params) { return chooseNumber(sa, string, min, max); - }; + } public final boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice) { return chooseBinary(sa, question, kindOfChoice, (Boolean) null); } public abstract boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Boolean defaultChioce); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerExerted.java b/forge-game/src/main/java/forge/game/trigger/TriggerExerted.java new file mode 100644 index 00000000000..8e42667f066 --- /dev/null +++ b/forge-game/src/main/java/forge/game/trigger/TriggerExerted.java @@ -0,0 +1,47 @@ +package forge.game.trigger; + +import forge.game.card.Card; +import forge.game.spellability.SpellAbility; + +import java.util.HashMap; +import java.util.Map; + +public class TriggerExerted extends Trigger { + /** + *

+ * Constructor for Trigger. + *

+ * + * @param params a {@link HashMap} object. + * @param host a {@link Card} object. + * @param intrinsic + */ + public TriggerExerted(Map params, Card host, boolean intrinsic) { + super(params, host, intrinsic); + } + + @Override + public boolean performTest(Map runParams2) { + final Card exerter = (Card) runParams2.get("Card"); + if (this.mapParams.containsKey("ValidCard")) { + if (!exerter.isValid(this.mapParams.get("ValidCard").split(","), this.getHostCard().getController(), + this.getHostCard(), null)) { + return false; + } + } + return true; + } + + @Override + public void setTriggeringObjects(SpellAbility sa) { + sa.setTriggeringObject("Card", this.getRunParams().get("Card")); + sa.setTriggeringObject("Player", this.getRunParams().get("Player")); + } + + @Override + public String getImportantStackObjects(SpellAbility sa) { + StringBuilder sb = new StringBuilder(); + sb.append("Exerted: ").append(sa.getTriggeringObject("Card")); + return sb.toString(); + } +} diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerType.java b/forge-game/src/main/java/forge/game/trigger/TriggerType.java index be84f45fbfc..78990fc2e58 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java @@ -47,6 +47,7 @@ public enum TriggerType { Discarded(TriggerDiscarded.class), Drawn(TriggerDrawn.class), Evolved(TriggerEvolved.class), + Exerted(TriggerExerted.class), Exploited(TriggerExploited.class), Fight(TriggerFight.class), FlippedCoin(TriggerFlippedCoin.class), @@ -104,7 +105,7 @@ public enum TriggerType { /** * TODO: Write javadoc for this method. - * @param string + * @param value * @return */ public static TriggerType smartValueOf(String value) { diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index 1575798bbec..4fd30b67a82 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -221,6 +221,11 @@ public class PlayerControllerForTests extends PlayerController { return blockers; } + @Override + public List exertAttackers(List attackers) { + return Lists.newArrayList(attackers); + } + @Override public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) { final CardCollection allBlockers = new CardCollection(oldBlockers); diff --git a/forge-gui/res/cardsfolder/upcoming/watchful_naga.txt b/forge-gui/res/cardsfolder/upcoming/watchful_naga.txt new file mode 100644 index 00000000000..af0f6861c39 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/watchful_naga.txt @@ -0,0 +1,8 @@ +Name:Watchful Naga +ManaCost:2 G +Types:Creature Naga Wizard +PT:2/2 +K:You may exert CARDNAME as it attacks. +T:Mode$ Exerted | ValidCard$ Card.Self | Execute$ DBDraw | TriggerDescription$ When you exert CARDNAME, draw a card. +SVar:DBDraw:DB$ Draw | Defined$ TriggeredPlayer +Oracle:You may exert Watchful Naga as it attacks. When you do, draw a card. \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 2f54586542e..f5b6d2c7582 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -576,6 +576,11 @@ public class PlayerControllerHuman return game.getCardList(getGui().order("Choose Damage Order for " + vAttacker, "Damaged First", CardView.getCollection(blockers), vAttacker)); } + @Override + public List exertAttackers(List attackers) { + return getGui().getChoices("Exert Attackers?", 0, attackers.size(), attackers); + } + @Override public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) { final CardView vAttacker = CardView.get(attacker);