diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index cd65766da72..4598da340e6 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import forge.game.cost.*; import org.apache.commons.lang3.ObjectUtils; import com.google.common.base.Predicate; @@ -23,36 +24,6 @@ import forge.game.card.CardLists; import forge.game.card.CardPredicates; import forge.game.card.CounterEnumType; import forge.game.card.CounterType; -import forge.game.cost.CostAddMana; -import forge.game.cost.CostChooseCreatureType; -import forge.game.cost.CostDamage; -import forge.game.cost.CostDecisionMakerBase; -import forge.game.cost.CostDiscard; -import forge.game.cost.CostDraw; -import forge.game.cost.CostExert; -import forge.game.cost.CostExile; -import forge.game.cost.CostExileFromStack; -import forge.game.cost.CostExiledMoveToGrave; -import forge.game.cost.CostFlipCoin; -import forge.game.cost.CostGainControl; -import forge.game.cost.CostGainLife; -import forge.game.cost.CostMill; -import forge.game.cost.CostPartMana; -import forge.game.cost.CostPayEnergy; -import forge.game.cost.CostPayLife; -import forge.game.cost.CostPutCardToLib; -import forge.game.cost.CostPutCounter; -import forge.game.cost.CostRemoveAnyCounter; -import forge.game.cost.CostRemoveCounter; -import forge.game.cost.CostReturn; -import forge.game.cost.CostReveal; -import forge.game.cost.CostSacrifice; -import forge.game.cost.CostTap; -import forge.game.cost.CostTapType; -import forge.game.cost.CostUnattach; -import forge.game.cost.CostUntap; -import forge.game.cost.CostUntapType; -import forge.game.cost.PaymentDecision; import forge.game.keyword.Keyword; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -577,6 +548,11 @@ public class AiCostDecision extends CostDecisionMakerBase { return PaymentDecision.card(aic.getCardsToDiscard(c, type.split(";"), ability)); } + @Override + public PaymentDecision visit(CostRevealChosenPlayer cost) { + return PaymentDecision.number(1); + } + protected int removeCounter(GameEntityCounterTable table, List prefs, CounterEnumType cType, int stillToRemove) { int removed = 0; if (!prefs.isEmpty() && stillToRemove > 0) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java index 9e238b1ff99..53f1b4f61d3 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java @@ -53,7 +53,11 @@ public class ChoosePlayerEffect extends SpellAbilityEffect { chosen = choices.isEmpty() ? null : p.getController().chooseSingleEntityForEffect(choices, sa, choiceDesc, null); } if( null != chosen ) { - card.setChosenPlayer(chosen); + if (sa.hasParam("Secretly")) { + card.setSecretChosenPlayer(chosen); + } else { + card.setChosenPlayer(chosen); + } if (sa.hasParam("ForgetOtherRemembered")) { card.clearRemembered(); } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 242ea938250..ad1a8e929ea 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1660,6 +1660,12 @@ public class Card extends GameEntity implements Comparable, IHasSVars { chosenPlayer = p; view.updateChosenPlayer(this); } + public final void setSecretChosenPlayer(final Player p) { + chosenPlayer = p; + } + public final void revealChosenPlayer() { + view.updateChosenPlayer(this); + } public final boolean hasChosenNumber() { return chosenNumber != null; diff --git a/forge-game/src/main/java/forge/game/cost/Cost.java b/forge-game/src/main/java/forge/game/cost/Cost.java index 1b05fa48913..0c5e4d28e61 100644 --- a/forge-game/src/main/java/forge/game/cost/Cost.java +++ b/forge-game/src/main/java/forge/game/cost/Cost.java @@ -482,6 +482,10 @@ public class Cost implements Serializable { return new CostExert(splitStr[0], splitStr[1], description); } + if (parse.equals("RevealChosenPlayer")) { + return new CostRevealChosenPlayer(); + } + // These won't show up with multiples if (parse.equals("Untap") || parse.equals("Q")) { return new CostUntap(); diff --git a/forge-game/src/main/java/forge/game/cost/CostRevealChosenPlayer.java b/forge-game/src/main/java/forge/game/cost/CostRevealChosenPlayer.java new file mode 100644 index 00000000000..3e50343e0b1 --- /dev/null +++ b/forge-game/src/main/java/forge/game/cost/CostRevealChosenPlayer.java @@ -0,0 +1,68 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.game.cost; + +import forge.game.card.Card; +import forge.game.player.Player; +import forge.game.spellability.SpellAbility; + +public class CostRevealChosenPlayer extends CostPart { + + /** + * Serializables need a version ID. + */ + private static final long serialVersionUID = 1L; + + public CostRevealChosenPlayer() { } + + /* + * (non-Javadoc) + * + * @see forge.card.cost.CostPart#toString() + */ + @Override + public final String toString() { + return "Reveal the player you chose"; + } + + /* + * (non-Javadoc) + * + * @see + * forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility, + * forge.Card, forge.Player, forge.card.cost.Cost) + */ + @Override + public final boolean canPay(final SpellAbility ability, final Player activator) { + final Card source = ability.getHostCard(); + + return source.getChosenPlayer() != null && source.getTurnInController().equals(activator); + } + + @Override + public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) { + ability.getHostCard().revealChosenPlayer(); + return true; + } + + // Inputs + public T accept(ICostVisitor visitor) { + return visitor.visit(this); + } + +} diff --git a/forge-game/src/main/java/forge/game/cost/ICostVisitor.java b/forge-game/src/main/java/forge/game/cost/ICostVisitor.java index 9ca192502b3..1b46b741e7e 100644 --- a/forge-game/src/main/java/forge/game/cost/ICostVisitor.java +++ b/forge-game/src/main/java/forge/game/cost/ICostVisitor.java @@ -23,6 +23,7 @@ public interface ICostVisitor { T visit(CostSacrifice cost); T visit(CostReturn cost); T visit(CostReveal cost); + T visit(CostRevealChosenPlayer cost); T visit(CostRemoveAnyCounter cost); T visit(CostRemoveCounter cost); T visit(CostPutCounter cost); @@ -138,6 +139,11 @@ public interface ICostVisitor { return null; } + @Override + public T visit(CostRevealChosenPlayer cost) { + return null; + } + @Override public T visit(CostRemoveAnyCounter cost) { return null; diff --git a/forge-gui/res/cardsfolder/e/emissary_of_grudges.txt b/forge-gui/res/cardsfolder/e/emissary_of_grudges.txt new file mode 100644 index 00000000000..63433152dee --- /dev/null +++ b/forge-gui/res/cardsfolder/e/emissary_of_grudges.txt @@ -0,0 +1,10 @@ +Name:Emissary of Grudges +ManaCost:5 R +Types:Creature Efreet +PT:6/5 +K:Flying +K:Haste +K:ETBReplacement:Other:ChooseP +SVar:ChooseP:DB$ ChoosePlayer | Defined$ You | Choices$ Player.Opponent | ChoiceTitle$ Choose an opponent | Secretly$ True | SpellDescription$ As CARDNAME enters the battlefield, secretly choose an opponent. +A:AB$ ChangeTargets | Cost$ RevealChosenPlayer | TargetType$ Spell,Activated,Triggered | ValidTgts$ Card | ConditionTargetValidTargeting$ Permanent.YouCtrl,You | ConditionPlayerDefined$ TargetedController | ConditionPlayerContains$ Player.Chosen | GameActivationLimit$ 1 | SpellDescription$ Choose new targets for target spell or ability if it's controlled by the chosen player and if it targets you or a permanent you control. Activate this ability only once. +Oracle:Flying, haste\nAs Emissary of Grudges enters the battlefield, secretly choose an opponent.\nReveal the player you chose: Choose new targets for target spell or ability if it’s controlled by the chosen player and if it targets you or a permanent you control. Activate this ability only once. diff --git a/forge-gui/res/cardsfolder/s/stalking_leonin.txt b/forge-gui/res/cardsfolder/s/stalking_leonin.txt new file mode 100644 index 00000000000..cb4257f5782 --- /dev/null +++ b/forge-gui/res/cardsfolder/s/stalking_leonin.txt @@ -0,0 +1,8 @@ +Name:Stalking Leonin +ManaCost:2 W +Types:Creature Cat Archer +PT:3/3 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChooseOpp | TriggerDescription$ When CARDNAME enters the battlefield, secretly choose an opponent. +SVar:TrigChooseOpp:DB$ ChoosePlayer | Defined$ You | Choices$ Player.Opponent | Secretly$ True +A:AB$ ChangeZone | Cost$ RevealChosenPlayer | ValidTgts$ Creature.attackingYou | TgtPrompt$ Select target creature that's attacking you. | Origin$ Battlefield | Destination$ Exile | ConditionDefined$ Targeted | ConditionPresent$ Card.ChosenCtrl | GameActivationLimit$ 1 | SpellDescription$ Exile target creature that's attacking you if it's controlled by the chosen player. Activate this ability only once. +Oracle:When Stalking Leonin enters the battlefield, secretly choose an opponent.\nReveal the player you chose: Exile target creature that's attacking you if it's controlled by the chosen player. Activate this ability only once. diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 841897a13a8..c7e34a9aa4d 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -27,37 +27,7 @@ import forge.game.card.CardPredicates.Presets; import forge.game.card.CardView; import forge.game.card.CounterEnumType; import forge.game.card.CounterType; -import forge.game.cost.CostAddMana; -import forge.game.cost.CostChooseCreatureType; -import forge.game.cost.CostDamage; -import forge.game.cost.CostDecisionMakerBase; -import forge.game.cost.CostDiscard; -import forge.game.cost.CostDraw; -import forge.game.cost.CostExert; -import forge.game.cost.CostExile; -import forge.game.cost.CostExileFromStack; -import forge.game.cost.CostExiledMoveToGrave; -import forge.game.cost.CostFlipCoin; -import forge.game.cost.CostGainControl; -import forge.game.cost.CostGainLife; -import forge.game.cost.CostMill; -import forge.game.cost.CostPart; -import forge.game.cost.CostPartMana; -import forge.game.cost.CostPayEnergy; -import forge.game.cost.CostPayLife; -import forge.game.cost.CostPutCardToLib; -import forge.game.cost.CostPutCounter; -import forge.game.cost.CostRemoveAnyCounter; -import forge.game.cost.CostRemoveCounter; -import forge.game.cost.CostReturn; -import forge.game.cost.CostReveal; -import forge.game.cost.CostSacrifice; -import forge.game.cost.CostTap; -import forge.game.cost.CostTapType; -import forge.game.cost.CostUnattach; -import forge.game.cost.CostUntap; -import forge.game.cost.CostUntapType; -import forge.game.cost.PaymentDecision; +import forge.game.cost.*; import forge.game.player.Player; import forge.game.player.PlayerView; import forge.game.spellability.SpellAbility; @@ -829,6 +799,11 @@ public class HumanCostDecision extends CostDecisionMakerBase { return PaymentDecision.card(inp.getSelected()); } + @Override + public PaymentDecision visit(final CostRevealChosenPlayer cost) { + return PaymentDecision.number(1); + } + @Override public PaymentDecision visit(final CostRemoveAnyCounter cost) { Integer c = cost.convertAmount();