diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 4d6c9bfc153..e567ddd5827 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -498,7 +498,7 @@ public class AiCostDecision extends CostDecisionMakerBase { } @Override - public PaymentDecision visit(CostRevealChosenPlayer cost) { + public PaymentDecision visit(CostRevealChosen cost) { return PaymentDecision.number(1); } 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 9f4601d97a7..6d16d9d18b4 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 @@ -1,5 +1,6 @@ package forge.game.ability.effects; +import forge.game.Game; import forge.game.GameLogEntryType; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; @@ -27,12 +28,15 @@ public class ChoosePlayerEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { final Card card = sa.getHostCard(); + final Game game = card.getGame(); final FCollectionView choices = sa.hasParam("Choices") ? AbilityUtils.getDefinedPlayers( - card, sa.getParam("Choices"), sa) : sa.getActivatingPlayer().getGame().getPlayersInTurnOrder(); + card, sa.getParam("Choices"), sa) : game.getPlayersInTurnOrder(); - final String choiceDesc = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoosePlayer"); + final String choiceDesc = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : + Localizer.getInstance().getMessage("lblChoosePlayer"); final boolean random = sa.hasParam("Random"); + final boolean secret = sa.hasParam("Secretly"); for (final Player p : getTargetPlayers(sa)) { if (!p.isInGame()) { @@ -45,7 +49,7 @@ public class ChoosePlayerEffect extends SpellAbilityEffect { chosen = choices.isEmpty() ? null : p.getController().chooseSingleEntityForEffect(choices, sa, choiceDesc, sa.hasParam("Optional"), null); } if (null != chosen) { - if (sa.hasParam("Secretly")) { + if (secret) { card.setSecretChosenPlayer(chosen); } else if (sa.hasParam("Protect")) { card.setProtectingPlayer(chosen); @@ -58,11 +62,10 @@ public class ChoosePlayerEffect extends SpellAbilityEffect { if (sa.hasParam("RememberChosen")) { card.addRemembered(chosen); } - if (sa.hasParam("DontNotify")) { //ie Shared Fate - //log the chosen player - p.getGame().getGameLog().add(GameLogEntryType.INFORMATION, Localizer.getInstance().getMessage("lblPlayerPickedChosen", sa.getActivatingPlayer(), chosen)); - } else { - p.getGame().getAction().notifyOfValue(sa, p, Localizer.getInstance().getMessage("lblPlayerPickedChosen", sa.getActivatingPlayer(), chosen), null); + if (!secret) { + //ie Shared Fate – log the chosen player + if (sa.hasParam("DontNotify")) game.getGameLog().add(GameLogEntryType.INFORMATION, Localizer.getInstance().getMessage("lblPlayerPickedChosen", sa.getActivatingPlayer(), chosen)); + else game.getAction().notifyOfValue(sa, p, Localizer.getInstance().getMessage("lblPlayerPickedChosen", sa.getActivatingPlayer(), chosen), null); } // SubAbility that only fires if a player is chosen SpellAbility chosenSA = sa.getAdditionalAbility("ChooseSubAbility"); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java index 7d5a2dbaeed..c81e441a8ef 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java @@ -41,6 +41,7 @@ public class ChooseTypeEffect extends SpellAbilityEffect { final List invalidTypes = sa.hasParam("InvalidTypes") ? Arrays.asList(sa.getParam("InvalidTypes").split(",")) : new ArrayList<>(); final List validTypes = new ArrayList<>(); final List tgtPlayers = getTargetPlayers(sa); + final boolean secret = sa.hasParam("Secretly"); if (sa.hasParam("ValidTypes")) { validTypes.addAll(Arrays.asList(sa.getParam("ValidTypes").split(","))); @@ -131,7 +132,7 @@ public class ChooseTypeEffect extends SpellAbilityEffect { choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes); } - p.getGame().getAction().notifyOfValue(sa, p, choice, noNotify); + if (!secret) p.getGame().getAction().notifyOfValue(sa, p, choice, noNotify); if (sa.hasParam("Note")) { card.addNotedType(choice); @@ -142,7 +143,8 @@ public class ChooseTypeEffect extends SpellAbilityEffect { if (sa.hasParam("ChooseType2")) { card.setChosenType2(choice); } else { - card.setChosenType(choice); + if (secret) card.setSecretChosenType(choice); + else card.setChosenType(choice); } } } else { 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 6e585649092..1f0c968e25e 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1877,6 +1877,13 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return chosenType != null && !chosenType.isEmpty(); } + public final void setSecretChosenType(final String s) { + chosenType = s; + } + public final void revealChosenType() { + view.updateChosenType(this); + } + // used by card Illusionary Terrain public final String getChosenType2() { return chosenType2; 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 de6cafa4dc8..50737312b56 100644 --- a/forge-game/src/main/java/forge/game/cost/Cost.java +++ b/forge-game/src/main/java/forge/game/cost/Cost.java @@ -543,8 +543,9 @@ public class Cost implements Serializable { return new CostEnlist(splitStr[0], splitStr[1], description); } - if (parse.equals("RevealChosenPlayer")) { - return new CostRevealChosenPlayer(); + if (parse.startsWith("RevealChosen<")) { + final String[] splitStr = abCostParse(parse, 2); + return new CostRevealChosen(splitStr[0], splitStr.length > 1 ? splitStr[1] : null); } // These won't show up with multiples diff --git a/forge-game/src/main/java/forge/game/cost/CostRevealChosenPlayer.java b/forge-game/src/main/java/forge/game/cost/CostRevealChosen.java similarity index 53% rename from forge-game/src/main/java/forge/game/cost/CostRevealChosenPlayer.java rename to forge-game/src/main/java/forge/game/cost/CostRevealChosen.java index 69378dc58c2..b7c80d77f39 100644 --- a/forge-game/src/main/java/forge/game/cost/CostRevealChosenPlayer.java +++ b/forge-game/src/main/java/forge/game/cost/CostRevealChosen.java @@ -20,12 +20,18 @@ package forge.game.cost; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.util.Localizer; -public class CostRevealChosenPlayer extends CostPart { +public class CostRevealChosen extends CostPart { private static final long serialVersionUID = 1L; - public CostRevealChosenPlayer() { } + public CostRevealChosen(final String type, final String desc) { + super("1", type, desc); + } + + @Override + public int paymentOrder() { return 20; } /* * (non-Javadoc) @@ -34,19 +40,39 @@ public class CostRevealChosenPlayer extends CostPart { */ @Override public final String toString() { - return "Reveal the player you chose"; + if (getType().equals("Player")) { + return "Reveal the player you chose"; + } else if (getType().equals("Type")) { + return "Reveal the chosen " + getDescriptiveType().toLowerCase(); + } + return "Update CostRevealChosen.java"; } @Override public final boolean canPay(final SpellAbility ability, final Player activator, final boolean effect) { final Card source = ability.getHostCard(); - return source.hasChosenPlayer() && source.getTurnInController().equals(activator); + if (getType().equals("Player")) { + return source.hasChosenPlayer() && source.getTurnInController().equals(activator); + } else if (getType().equals("Type")) { + return source.hasChosenType() && source.getTurnInController().equals(activator); + } + return false; } @Override public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability, final boolean effect) { - ability.getHostCard().revealChosenPlayer(); + Card host = ability.getHostCard(); + String o = ""; + if (getType().equals("Player")) { + o = host.getChosenPlayer().toString(); + host.revealChosenPlayer(); + } else if (getType().equals("Type")) { + o = host.getChosenType(); + host.revealChosenType(); + } + final String message = Localizer.getInstance().getMessage("lblPlayerReveals", ai, o); + ai.getGame().getAction().notifyOfValue(ability, host, message, ai); return true; } 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 b7fd0acc468..3ccb159e3c3 100644 --- a/forge-game/src/main/java/forge/game/cost/ICostVisitor.java +++ b/forge-game/src/main/java/forge/game/cost/ICostVisitor.java @@ -26,7 +26,7 @@ public interface ICostVisitor { T visit(CostSacrifice cost); T visit(CostReturn cost); T visit(CostReveal cost); - T visit(CostRevealChosenPlayer cost); + T visit(CostRevealChosen cost); T visit(CostRemoveAnyCounter cost); T visit(CostRemoveCounter cost); T visit(CostPutCounter cost); @@ -159,7 +159,7 @@ public interface ICostVisitor { } @Override - public T visit(CostRevealChosenPlayer cost) { + public T visit(CostRevealChosen 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 index 07d4d237d56..4b5b4c0f27b 100644 --- a/forge-gui/res/cardsfolder/e/emissary_of_grudges.txt +++ b/forge-gui/res/cardsfolder/e/emissary_of_grudges.txt @@ -5,6 +5,6 @@ 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,Emblem | ConditionTargetValidTargeting$ Permanent.YouCtrl+inRealZoneBattlefield,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 only once. +SVar:ChooseP:DB$ ChoosePlayer | Defined$ You | Choices$ Opponent | ChoiceTitle$ Choose an opponent | Secretly$ True | SpellDescription$ As CARDNAME enters the battlefield, secretly choose an opponent. +A:AB$ ChangeTargets | Cost$ RevealChosen | TargetType$ Spell,Activated,Triggered | ValidTgts$ Card,Emblem | ConditionTargetValidTargeting$ Permanent.YouCtrl+inRealZoneBattlefield,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 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 only once. diff --git a/forge-gui/res/cardsfolder/g/guardian_archon.txt b/forge-gui/res/cardsfolder/g/guardian_archon.txt index 85f63948d43..08699252398 100644 --- a/forge-gui/res/cardsfolder/g/guardian_archon.txt +++ b/forge-gui/res/cardsfolder/g/guardian_archon.txt @@ -4,7 +4,7 @@ Types:Creature Archon PT:5/5 K:Flying 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$ Pump | Cost$ RevealChosenPlayer | Defined$ You | KW$ Protection:Player.PlayerUID_ChosenPlayerUID:ChosenPlayerName | DefinedKW$ ChosenPlayer | SubAbility$ DBPump | GameActivationLimit$ 1 | StackDescription$ {p:You} and | SpellDescription$ You and target permanent you control each gain protection from the chosen player until end of turn. Activate only once. -SVar:DBPump:DB$ Pump | ValidTgts$ Permanent.YouCtrl | TgtPrompt$ Select target permanent you control | KW$ Protection:Player.PlayerUID_ChosenPlayerUID:ChosenPlayerName | DefinedKW$ ChosenPlayer | StackDescription$ {c:Targeted} each gain protection from {p:ChosenPlayer} until end of turn. Activate only once. +SVar:ChooseP:DB$ ChoosePlayer | Defined$ You | Choices$ Opponent | ChoiceTitle$ Choose an opponent | Secretly$ True | SpellDescription$ As CARDNAME enters the battlefield, secretly choose an opponent. +A:AB$ Pump | Cost$ RevealChosen | ValidTgts$ Permanent.YouCtrl | TgtPrompt$ Select target permanent you control | KW$ Protection:Player.PlayerUID_ChosenPlayerUID:ChosenPlayerName | DefinedKW$ ChosenPlayer | GameActivationLimit$ 1 | SubAbility$ DBPump | StackDescription$ {p:You} and {c:Targeted} each gain protection from {p:ChosenPlayer} until end of turn. | SpellDescription$ You and target permanent you control each gain protection from the chosen player until end of turn. Activate only once. +SVar:DBPump:DB$ Pump | Defined$ You | KW$ Protection:Player.PlayerUID_ChosenPlayerUID:ChosenPlayerName | DefinedKW$ ChosenPlayer | StackDescription$ None Oracle:Flying\nAs Guardian Archon enters the battlefield, secretly choose an opponent.\nReveal the player you chose: You and target permanent you control each gain protection from the chosen player until end of turn. Activate only once. diff --git a/forge-gui/res/cardsfolder/s/stalking_leonin.txt b/forge-gui/res/cardsfolder/s/stalking_leonin.txt index ad40e25e1f3..7788820e34c 100644 --- a/forge-gui/res/cardsfolder/s/stalking_leonin.txt +++ b/forge-gui/res/cardsfolder/s/stalking_leonin.txt @@ -3,6 +3,6 @@ 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 only once. +SVar:TrigChooseOpp:DB$ ChoosePlayer | Defined$ You | Choices$ Opponent | Secretly$ True +A:AB$ ChangeZone | Cost$ RevealChosen | 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 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 only once. diff --git a/forge-gui/res/cardsfolder/upcoming/a_killer_among_us.txt b/forge-gui/res/cardsfolder/upcoming/a_killer_among_us.txt new file mode 100644 index 00000000000..457fb7656db --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/a_killer_among_us.txt @@ -0,0 +1,10 @@ +Name:A Killer Among Us +ManaCost:4 G +Types:Enchantment +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a 1/1 white Human creature token, a 1/1 blue Merfolk creature token, and a 1/1 red Goblin creature token. Then secretly choose Human, Merfolk, or Goblin. +SVar:TrigToken:DB$ Token | TokenScript$ w_1_1_human,u_1_1_merfolk,r_1_1_goblin | SubAbility$ DBChooseType +SVar:DBChooseType:DB$ ChooseType | Type$ Creature | ValidTypes$ Human,Merfolk,Goblin | Secretly$ True +A:AB$ PutCounter | Cost$ Sac<1/CARDNAME> RevealChosen | ValidTgts$ Creature.token+attacking | TgtPrompt$ Select target attacking creature token | AITgts$ Creature.token+attacking+ChosenType | Defined$ Targeted.ChosenType | CounterType$ P1P1 | CounterNum$ 3 | SubAbility$ DBPump | StackDescription$ REP target attacking creature token_{c:Targeted} | SpellDescription$ If target attacking creature token is the chosen type, put three +1/+1 counters on it +SVar:DBPump:DB$ Pump | Defined$ Targeted.ChosenType | KW$ Deathtouch | StackDescription$ SpellDescription | SpellDescription$ and it gains deathtouch until end of turn. +DeckHas:Ability$Token|Sacrifice & Type$Human|Merfolk|Goblin & Color$White|Blue|Red +Oracle:When A Killer Among Us enters the battlefield, create a 1/1 white Human creature token, a 1/1 blue Merfolk creature token, and a 1/1 red Goblin creature token. Then secretly choose Human, Merfolk, or Goblin.\nSacrifice A Killer Among Us, Reveal the chosen creature type: If target attacking creature token is the chosen type, put three +1/+1 counters on it and it gains deathtouch until end of turn. diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 83e9270de52..fe87f2e2035 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -2433,6 +2433,8 @@ lblThisPlaneHasNoDesc=This plane has no description. #ConquestUtil.java lblHistoriiansWillRecallYourConquestAs=Historians will recall your conquest as: lblConquestName=Conquest Name +#CostRevealChosen.java +lblPlayerReveals={0} reveals {1} #HumanCostDecision.java lblChooseXValueForCard={0} - Choose a Value for X lblSelectOneSameNameCardToDiscardAlreadyChosen=Select one of the cards with the same name to discard. Already chosen: diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 3354ba0b7ea..075be940a16 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -851,7 +851,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { } @Override - public PaymentDecision visit(final CostRevealChosenPlayer cost) { + public PaymentDecision visit(final CostRevealChosen cost) { return PaymentDecision.number(1); }