From f535c8bc8e254888089c3d63d2d8991ac03d9503 Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Fri, 22 Oct 2021 12:45:14 +0000 Subject: [PATCH] MicroProse Astral cards!?! --- .../java/forge/ai/PlayerControllerAi.java | 1 - .../src/main/java/forge/game/GameAction.java | 11 +-- .../java/forge/game/GameLogFormatter.java | 38 +++++----- .../forge/game/ability/AbilityFactory.java | 9 +-- .../main/java/forge/game/ability/ApiType.java | 1 + .../ability/effects/ChooseCardEffect.java | 20 ++++- .../ability/effects/ChooseColorEffect.java | 18 ++++- .../ability/effects/ChooseEntityEffect.java | 54 ++++++++++++++ .../ability/effects/ChooseGenericEffect.java | 10 ++- .../ability/effects/ChooseTypeEffect.java | 29 ++++++-- .../game/ability/effects/CleanUpEffect.java | 48 ++++++++++++ .../ability/effects/CountersPutEffect.java | 73 ++++++++++++++++--- .../java/forge/game/card/CounterEnumType.java | 2 + .../game/event/GameEventCardModeChosen.java | 4 +- .../forge/game/event/GameEventRandomLog.java | 15 ++++ .../forge/game/event/IGameEventVisitor.java | 2 + .../game/spellability/TargetRestrictions.java | 16 ++++ .../src/main/java/forge/util/MessageUtil.java | 18 ++++- forge-gui/res/cardsfolder/a/aswan_jaguar.txt | 8 ++ .../res/cardsfolder/c/call_from_the_grave.txt | 8 ++ forge-gui/res/cardsfolder/f/faerie_dragon.txt | 56 ++++++++++++++ forge-gui/res/cardsfolder/g/gem_bazaar.txt | 7 ++ .../res/cardsfolder/g/goblin_polka_band.txt | 10 +++ .../res/cardsfolder/g/power_struggle.txt | 7 ++ .../res/cardsfolder/n/necropolis_of_azar.txt | 10 +++ .../res/cardsfolder/o/orcish_catapult.txt | 6 ++ forge-gui/res/cardsfolder/p/pandoras_box.txt | 9 +++ .../res/cardsfolder/p/prismatic_dragon.txt | 11 +++ .../res/cardsfolder/r/rainbow_knights.txt | 11 +++ forge-gui/res/cardsfolder/w/whimsy.txt | 47 ++++++++++++ forge-gui/res/editions/Astral Cards.txt | 19 +++++ forge-gui/res/languages/de-DE.properties | 8 ++ forge-gui/res/languages/en-US.properties | 10 ++- forge-gui/res/languages/es-ES.properties | 8 ++ forge-gui/res/languages/it-IT.properties | 8 ++ forge-gui/res/languages/ja-JP.properties | 8 ++ forge-gui/res/languages/zh-CN.properties | 8 ++ forge-gui/res/tokenscripts/spawn_of_azar.txt | 7 ++ .../forge/player/HumanPlaySpellAbility.java | 2 +- .../java/forge/player/TargetSelection.java | 17 ++++- 40 files changed, 590 insertions(+), 64 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/ability/effects/ChooseEntityEffect.java create mode 100644 forge-game/src/main/java/forge/game/event/GameEventRandomLog.java create mode 100644 forge-gui/res/cardsfolder/a/aswan_jaguar.txt create mode 100644 forge-gui/res/cardsfolder/c/call_from_the_grave.txt create mode 100644 forge-gui/res/cardsfolder/f/faerie_dragon.txt create mode 100644 forge-gui/res/cardsfolder/g/gem_bazaar.txt create mode 100644 forge-gui/res/cardsfolder/g/goblin_polka_band.txt create mode 100644 forge-gui/res/cardsfolder/g/power_struggle.txt create mode 100644 forge-gui/res/cardsfolder/n/necropolis_of_azar.txt create mode 100644 forge-gui/res/cardsfolder/o/orcish_catapult.txt create mode 100644 forge-gui/res/cardsfolder/p/pandoras_box.txt create mode 100644 forge-gui/res/cardsfolder/p/prismatic_dragon.txt create mode 100644 forge-gui/res/cardsfolder/r/rainbow_knights.txt create mode 100644 forge-gui/res/cardsfolder/w/whimsy.txt create mode 100644 forge-gui/res/editions/Astral Cards.txt create mode 100644 forge-gui/res/tokenscripts/spawn_of_azar.txt diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index c17c0956d94..fee43f50e54 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -578,7 +578,6 @@ public class PlayerControllerAi extends PlayerController { chosen = validTypes.iterator().next(); System.err.println("AI has no idea how to choose " + kindOfType +", defaulting to arbitrary element: chosen"); } - getGame().getAction().notifyOfValue(sa, player, chosen, player); return chosen; } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 4e873a7604d..427e372557b 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import forge.util.*; import org.apache.commons.lang3.tuple.ImmutablePair; import com.google.common.base.Predicate; @@ -83,13 +84,6 @@ import forge.game.zone.PlayerZoneBattlefield; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.item.PaperCard; -import forge.util.Aggregates; -import forge.util.CardTranslation; -import forge.util.Expressions; -import forge.util.Localizer; -import forge.util.MyRandom; -import forge.util.ThreadUtil; -import forge.util.Visitor; import forge.util.collect.FCollection; import forge.util.collect.FCollectionView; @@ -1891,6 +1885,9 @@ public class GameAction { /** Delivers a message to all players. (use reveal to show Cards) */ public void notifyOfValue(SpellAbility saSource, GameObject relatedTarget, String value, Player playerExcept) { + String name = CardTranslation.getTranslatedName(saSource.getHostCard().getName()); + value = TextUtil.fastReplace(value, "CARDNAME", name); + value = TextUtil.fastReplace(value, "NICKNAME", Lang.getInstance().getNickName(name)); for (Player p : game.getPlayers()) { if (playerExcept == p) continue; p.getController().notifyOfValue(saSource, relatedTarget, value); diff --git a/forge-game/src/main/java/forge/game/GameLogFormatter.java b/forge-game/src/main/java/forge/game/GameLogFormatter.java index a05458ec288..c61515160b9 100644 --- a/forge-game/src/main/java/forge/game/GameLogFormatter.java +++ b/forge-game/src/main/java/forge/game/GameLogFormatter.java @@ -10,31 +10,16 @@ import com.google.common.eventbus.Subscribe; import forge.LobbyPlayer; import forge.game.card.Card; -import forge.game.event.GameEvent; -import forge.game.event.GameEventAttackersDeclared; -import forge.game.event.GameEventBlockersDeclared; -import forge.game.event.GameEventCardDamaged; +import forge.game.event.*; import forge.game.event.GameEventCardDamaged.DamageType; -import forge.game.event.GameEventCardModeChosen; -import forge.game.event.GameEventGameOutcome; -import forge.game.event.GameEventLandPlayed; -import forge.game.event.GameEventMulligan; -import forge.game.event.GameEventPlayerControl; -import forge.game.event.GameEventPlayerDamaged; -import forge.game.event.GameEventPlayerPoisoned; -import forge.game.event.GameEventScry; -import forge.game.event.GameEventSpellAbilityCast; -import forge.game.event.GameEventSpellResolved; -import forge.game.event.GameEventSurveil; -import forge.game.event.GameEventTurnBegan; -import forge.game.event.GameEventTurnPhase; -import forge.game.event.IGameEventVisitor; import forge.game.player.Player; import forge.game.player.RegisteredPlayer; import forge.game.spellability.TargetChoices; import forge.game.zone.ZoneType; +import forge.util.CardTranslation; import forge.util.Lang; import forge.util.Localizer; +import forge.util.TextUtil; import forge.util.maps.MapOfLists; public class GameLogFormatter extends IGameEventVisitor.Base { @@ -128,10 +113,25 @@ public class GameLogFormatter extends IGameEventVisitor.Base { return null; } - String modeChoiceOutcome = Localizer.getInstance().getMessage("lblLogPlayerChosenModeForCard", ev.player.toString(), ev.mode, ev.cardName); + String modeChoiceOutcome; + if (ev.random) { + modeChoiceOutcome = Localizer.getInstance().getMessage("lblLogRandomMode", ev.cardName, ev.mode); + } else { + modeChoiceOutcome = Localizer.getInstance().getMessage("lblLogPlayerChosenModeForCard", + ev.player.toString(), ev.mode, ev.cardName); + } + String name = CardTranslation.getTranslatedName(ev.cardName); + modeChoiceOutcome = TextUtil.fastReplace(modeChoiceOutcome, "CARDNAME", name); + modeChoiceOutcome = TextUtil.fastReplace(modeChoiceOutcome, "NICKNAME", + Lang.getInstance().getNickName(name)); return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, modeChoiceOutcome); } + @Override + public GameLogEntry visit(GameEventRandomLog ev) { + return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, ev.message); + } + private static GameLogEntry generateSummary(final Collection gamesPlayed) { final GameOutcome outcome1 = Iterables.getFirst(gamesPlayed, null); final HashMap players = outcome1.getPlayerNames(); diff --git a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java index 26907d37024..b96892c8fc9 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java @@ -32,11 +32,7 @@ import forge.game.ability.effects.RollDiceEffect; import forge.game.card.Card; import forge.game.card.CardState; import forge.game.cost.Cost; -import forge.game.spellability.AbilitySub; -import forge.game.spellability.SpellAbility; -import forge.game.spellability.SpellAbilityCondition; -import forge.game.spellability.SpellAbilityRestriction; -import forge.game.spellability.TargetRestrictions; +import forge.game.spellability.*; import forge.game.zone.ZoneType; import forge.util.FileSection; import io.sentry.Sentry; @@ -393,6 +389,9 @@ public final class AbilityFactory { if (mapParams.containsKey("TargetsAtRandom")) { abTgt.setRandomTarget(true); } + if (mapParams.containsKey("RandomNumTargets")) { + abTgt.setRandomNumTargets(true); + } if (mapParams.containsKey("TargetingPlayer")) { abTgt.setMandatory(true); } diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java index 1d8b282189c..2cfe9ebf9e1 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -41,6 +41,7 @@ public enum ApiType { ChooseCard (ChooseCardEffect.class), ChooseColor (ChooseColorEffect.class), ChooseDirection (ChooseDirectionEffect.class), + ChooseEntity (ChooseEntityEffect.class), ChooseEvenOdd (ChooseEvenOddEffect.class), ChooseNumber (ChooseNumberEffect.class), ChoosePlayer (ChoosePlayerEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java index cfefd2f4ff2..28f17782120 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java @@ -55,7 +55,7 @@ public class ChooseCardEffect extends SpellAbilityEffect { if (sa.hasParam("ChoiceZone")) { choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone")); } - CardCollectionView choices = game.getCardsIn(choiceZone); + CardCollectionView choices = sa.hasParam("AllCards") ? game.getCardsInGame() : game.getCardsIn(choiceZone); if (sa.hasParam("Choices")) { choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host, sa); } @@ -65,9 +65,23 @@ public class ChooseCardEffect extends SpellAbilityEffect { if (sa.hasParam("DefinedCards")) { choices = AbilityUtils.getDefinedCards(host, sa.getParam("DefinedCards"), sa); } + if (sa.hasParam("IncludeSpellsOnStack")) { + CardCollectionView stack = game.getCardsIn(ZoneType.Stack); + CardCollection combined = new CardCollection(); + combined.addAll(stack); + combined.addAll(choices); + choices = combined; + } - final String numericAmount = sa.getParamOrDefault("Amount", "1"); - final int validAmount = StringUtils.isNumeric(numericAmount) ? Integer.parseInt(numericAmount) : AbilityUtils.calculateAmount(host, numericAmount, sa); + final String amountValue = sa.getParamOrDefault("Amount", "1"); + int validAmount; + if (StringUtils.isNumeric(amountValue)) { + validAmount = Integer.parseInt(amountValue); + } else if (amountValue.equals("Random")) { + validAmount = Aggregates.randomInt(0, choices.size()); + } else { + validAmount = AbilityUtils.calculateAmount(host, amountValue, sa); + } final int minAmount = sa.hasParam("MinAmount") ? Integer.parseInt(sa.getParam("MinAmount")) : validAmount; if (validAmount <= 0) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java index 475fb2357b3..3775d25a32c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java @@ -10,6 +10,7 @@ import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; +import forge.util.Aggregates; import forge.util.Lang; import forge.util.Localizer; @@ -52,7 +53,7 @@ public class ChooseColorEffect extends SpellAbilityEffect { for (final Player p : tgtPlayers) { if ((tgt == null) || p.canBeTargetedBy(sa)) { - List chosenColors; + List chosenColors = new ArrayList<>(); int cntMin = sa.hasParam("TwoColors") ? 2 : 1; int cntMax = sa.hasParam("TwoColors") ? 2 : sa.hasParam("OrColors") ? colorChoices.size() : 1; String prompt = null; @@ -69,12 +70,23 @@ public class ChooseColorEffect extends SpellAbilityEffect { prompt = Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(cntMax)); } } - chosenColors = p.getController().chooseColors(prompt, sa, cntMin, cntMax, colorChoices); + Player noNotify = p; + if (sa.hasParam("Random")) { + String choice; + for (int i=0; i choices = Lists.newArrayList(); + String cardsDef = sa.getParam("CardChoices"); + String playersDef = sa.getParam("PlayerChoices"); + CardCollection cards = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), cardsDef, activator, + host, sa); + choices.addAll(cards); + PlayerCollection players = AbilityUtils.getDefinedPlayers(host, playersDef, sa); + choices.addAll(players); + + Object chosen = null; + if (sa.hasParam("Random")) { // currently we only choose at random for this + chosen = Aggregates.random(choices); + } + if (chosen == null) { + System.err.println("Error: ChooseEntityEffect.java unable to choose an entity"); + return; + } + + if (sa.hasParam("RememberChosen")) { + host.addRemembered(chosen); + } + } +} diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java index ccf5184c8d4..b249d308327 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java @@ -73,7 +73,9 @@ public class ChooseGenericEffect extends SpellAbilityEffect { List chosenSAs = Lists.newArrayList(); String prompt = sa.getParamOrDefault("ChoicePrompt","Choose"); + boolean random = false; if (sa.hasParam("AtRandom")) { + random = true; Aggregates.random(abilities, amount, chosenSAs); } else { chosenSAs = p.getController().chooseSpellAbilitiesForEffect(abilities, sa, prompt, amount, ImmutableMap.of()); @@ -89,15 +91,17 @@ public class ChooseGenericEffect extends SpellAbilityEffect { if (sa.hasParam("SetChosenMode")) { sa.getHostCard().setChosenMode(chosenValue); } - p.getGame().fireEvent(new GameEventCardModeChosen(p, host.getName(), chosenValue, sa.hasParam("ShowChoice"))); + p.getGame().fireEvent(new GameEventCardModeChosen(p, host.getName(), chosenValue, + sa.hasParam("ShowChoice"), random)); AbilityUtils.resolve(chosenSA); } } else { // no choices are valid, e.g. maybe all Unless costs are unpayable if (fallback != null) { - p.getGame().fireEvent(new GameEventCardModeChosen(p, host.getName(), fallback.getDescription(), sa.hasParam("ShowChoice"))); + p.getGame().fireEvent(new GameEventCardModeChosen(p, host.getName(), fallback.getDescription(), + sa.hasParam("ShowChoice"), random)); AbilityUtils.resolve(fallback); - } else if (!sa.hasParam("AtRandom")) { + } else if (!random) { System.err.println("Warning: all Unless costs were unpayable for " + host.getName() +", but it had no FallbackAbility defined. Doing nothing (this is most likely incorrect behavior)."); } } 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 4aae63ff01e..17850725c26 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 @@ -19,10 +19,14 @@ public class ChooseTypeEffect extends SpellAbilityEffect { protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); - for (final Player p : getTargetPlayers(sa)) { - sb.append(p).append(" "); + if (!sa.usesTargeting()) { + for (final Player p : getTargetPlayers(sa)) { + sb.append(p); + } + sb.append(" chooses a type."); + } else { + sb.append("Please improve the stack description."); } - sb.append("chooses a type."); return sb.toString(); } @@ -32,8 +36,9 @@ public class ChooseTypeEffect extends SpellAbilityEffect { final Card card = sa.getHostCard(); final String type = sa.getParam("Type"); final List invalidTypes = sa.hasParam("InvalidTypes") ? Arrays.asList(sa.getParam("InvalidTypes").split(",")) : new ArrayList<>(); - final List validTypes = new ArrayList<>(); + final List tgtPlayers = getTargetPlayers(sa); + if (sa.hasParam("ValidTypes")) { validTypes.addAll(Arrays.asList(sa.getParam("ValidTypes").split(","))); } @@ -52,6 +57,18 @@ public class ChooseTypeEffect extends SpellAbilityEffect { case "Land": validTypes.addAll(CardType.getAllLandTypes()); break; + case "CreatureInTargetedDeck": + for (final Player p : tgtPlayers) { + for (Card c : p.getAllCards()) { + if (c.getType().getCreatureTypes() != null) { + for (String s : c.getType().getCreatureTypes()) { + if (!validTypes.contains(s)) { + validTypes.add(s); + } + } + } + } + } } } @@ -60,14 +77,15 @@ public class ChooseTypeEffect extends SpellAbilityEffect { } final TargetRestrictions tgt = sa.getTargetRestrictions(); - final List tgtPlayers = getTargetPlayers(sa); if (!validTypes.isEmpty()) { for (final Player p : tgtPlayers) { String choice; if ((tgt == null) || p.canBeTargetedBy(sa)) { + Player noNotify = p; if (sa.hasParam("AtRandom")) { choice = Aggregates.random(validTypes); + noNotify = null; } else { choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes); } @@ -76,6 +94,7 @@ public class ChooseTypeEffect extends SpellAbilityEffect { } else { card.setChosenType2(choice); } + p.getGame().getAction().notifyOfValue(sa, p, choice, noNotify); } } } else { diff --git a/forge-game/src/main/java/forge/game/ability/effects/CleanUpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CleanUpEffect.java index 5847c068ed8..67949462d7e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CleanUpEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CleanUpEffect.java @@ -5,8 +5,11 @@ import forge.game.Game; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; +import forge.game.event.GameEventRandomLog; import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.util.CardTranslation; +import forge.util.Localizer; public class CleanUpEffect extends SpellAbilityEffect { @@ -18,6 +21,11 @@ public class CleanUpEffect extends SpellAbilityEffect { Card source = sa.getHostCard(); final Game game = source.getGame(); + String logMessage = ""; + if (sa.hasParam("Log")) { + logMessage = logOutput(sa, source); + } + if (sa.hasParam("ClearRemembered")) { source.clearRemembered(); game.getCardState(source).clearRemembered(); @@ -58,5 +66,45 @@ public class CleanUpEffect extends SpellAbilityEffect { if (sa.hasParam("ClearNamedCard")) { source.setNamedCard(""); } + if (sa.hasParam("Log")) { + source.getController().getGame().fireEvent(new GameEventRandomLog(logMessage)); + } + } + + protected String logOutput(SpellAbility sa, Card source) { + final StringBuilder log = new StringBuilder(); + final String name = CardTranslation.getTranslatedName(source.getName()); + String linebreak = "\r\n"; + + if (sa.hasParam("ClearRemembered") && source.getRememberedCount() != 0) { + for (Object o : source.getRemembered()) { + String rem = o.toString(); + if (o instanceof Card) { + log.append(log.length() > 0 ? linebreak : ""); + log.append(Localizer.getInstance().getMessage("lblChosenCard", name, rem)); + } else if (o instanceof Player) { + log.append(log.length() > 0 ? linebreak : ""); + log.append(Localizer.getInstance().getMessage("lblChosenPlayer", name, rem)); + } + } + } + + String chCard = sa.hasParam("ClearChosenCard") && source.hasChosenCard() ? source.getChosenCards() + .toString().replace("[","").replace("]", "") : ""; + if (chCard.length() > 0 && !log.toString().contains(chCard)) { + log.append(log.length() > 0 ? linebreak : ""); + String message = source.getChosenCards().size() > 1 ? "lblChosenMultiCard" : "lblChosenCard"; + log.append(Localizer.getInstance().getMessage(message, name, chCard)); + } + + String chPlay = sa.hasParam("ClearChosenPlayer") && source.hasChosenPlayer() + ? source.getChosenPlayer().toString() : ""; + if (chPlay.length() > 0 && !log.toString().contains(chPlay)) { + log.append(log.length() > 0 ? linebreak : ""); + log.append(Localizer.getInstance().getMessage("lblChosenPlayer", name, chPlay)); + } + log.append(log.length() > 0 ? "" : Localizer.getInstance().getMessage("lblNoValidChoice", name)); + + return log.toString(); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index f42c21939fe..6a1521b21a0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -1,9 +1,6 @@ package forge.game.ability.effects; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import com.google.common.collect.Iterables; @@ -27,6 +24,7 @@ import forge.game.card.CardPredicates; import forge.game.card.CardUtil; import forge.game.card.CounterEnumType; import forge.game.card.CounterType; +import forge.game.event.GameEventRandomLog; import forge.game.player.Player; import forge.game.player.PlayerActionConfirmMode; import forge.game.player.PlayerController; @@ -60,8 +58,11 @@ public class CountersPutEffect extends SpellAbilityEffect { stringBuilder.append("Bolster ").append(amount); return stringBuilder.toString(); } - if (spellAbility.isDividedAsYouChoose()) { + boolean divAsChoose = spellAbility.isDividedAsYouChoose(); + if (divAsChoose) { stringBuilder.append("Distribute "); + } else if (spellAbility.hasParam("DividedRandomly")){ + stringBuilder.append("Randomly distribute "); } else { stringBuilder.append("Put "); } @@ -77,7 +78,8 @@ public class CountersPutEffect extends SpellAbilityEffect { } stringBuilder.append(CounterType.getType(type).getName().toLowerCase()).append(" counter"); - stringBuilder.append(amount != 1 ? "s" : "").append(spellAbility.isDividedAsYouChoose() ? " among " : " on "); + stringBuilder.append(amount != 1 ? "s" : "").append(divAsChoose || spellAbility.hasParam("DividedRandomly") + ? " among " : " on "); // if use targeting we show all targets and corresponding counters if(spellAbility.usesTargeting()) { @@ -175,8 +177,12 @@ public class CountersPutEffect extends SpellAbilityEffect { Map params = Maps.newHashMap(); params.put("CounterType", counterType); - Iterables.addAll(tgtObjects, chooser.getController().chooseCardsForEffect(choices, sa, title, m, n, - sa.hasParam("ChoiceOptional"), params)); + if (sa.hasParam("DividedRandomly")) { + tgtObjects.addAll(choices); + } else { + Iterables.addAll(tgtObjects, chooser.getController().chooseCardsForEffect(choices, sa, title, m, n, + sa.hasParam("ChoiceOptional"), params)); + } } else { tgtObjects.addAll(getDefinedOrTargeted(sa, "Defined")); } @@ -187,7 +193,38 @@ public class CountersPutEffect extends SpellAbilityEffect { } int counterRemain = counterAmount; - for (final GameObject obj : tgtObjects) { + if (sa.hasParam("DividedRandomly")) { + CardCollection targets = new CardCollection(); + for (final GameObject obj : tgtObjects) { // check if each target is still OK + if (obj instanceof Card) { + Card tgtCard = (Card) obj; + Card gameCard = game.getCardState(tgtCard, null); + if (gameCard == null || !tgtCard.equalsWithTimestamp(gameCard)) { + tgtObjects.remove(obj); + } else { + targets.add(gameCard); + } + } else { // for now, we can remove non-card objects if they somehow got targeted + tgtObjects.remove(obj); + } + } + if (tgtObjects.size() == 0) { + return; + } + Map randomMap = Maps.newHashMap(); + for (int i=0; i randomMap, Card card) { + StringBuilder randomLog = new StringBuilder(); + randomLog.append(card.getName()).append(" randomly distributed "); + if (randomMap.entrySet().size() == 0) { + randomLog.append("no counters."); + } else { + randomLog.append("counters: "); + int count = 0; + for (Entry e : randomMap.entrySet()) { + count++; + randomLog.append(e.getKey()).append(" (").append(e.getValue()).append(" counter"); + randomLog.append(e.getValue() != 1 ? "s" : "").append(")"); + randomLog.append(count == randomMap.entrySet().size() ? "" : ", "); + } + } + return randomLog.toString(); + } } diff --git a/forge-game/src/main/java/forge/game/card/CounterEnumType.java b/forge-game/src/main/java/forge/game/card/CounterEnumType.java index 38dbafdf64e..0b56739c1cf 100644 --- a/forge-game/src/main/java/forge/game/card/CounterEnumType.java +++ b/forge-game/src/main/java/forge/game/card/CounterEnumType.java @@ -161,6 +161,8 @@ public enum CounterEnumType { HUNGER("HUNGR", 255, 91, 149), + HUSK("HUSK", 227, 212, 173), + ICE("ICE", 0, 239, 255), INCARNATION("INCRN", 247, 206, 64), diff --git a/forge-game/src/main/java/forge/game/event/GameEventCardModeChosen.java b/forge-game/src/main/java/forge/game/event/GameEventCardModeChosen.java index 24ce0c5f1bb..865cc2cfa4e 100644 --- a/forge-game/src/main/java/forge/game/event/GameEventCardModeChosen.java +++ b/forge-game/src/main/java/forge/game/event/GameEventCardModeChosen.java @@ -8,12 +8,14 @@ public class GameEventCardModeChosen extends GameEvent { public final String cardName; public final String mode; public final boolean log; + public final boolean random; - public GameEventCardModeChosen(Player player, String cardName, String mode, boolean log) { + public GameEventCardModeChosen(Player player, String cardName, String mode, boolean log, boolean random) { this.player = player; this.cardName = cardName; this.mode = mode; this.log = log; + this.random = random; } @Override diff --git a/forge-game/src/main/java/forge/game/event/GameEventRandomLog.java b/forge-game/src/main/java/forge/game/event/GameEventRandomLog.java new file mode 100644 index 00000000000..3fa778bbd74 --- /dev/null +++ b/forge-game/src/main/java/forge/game/event/GameEventRandomLog.java @@ -0,0 +1,15 @@ +package forge.game.event; + +public class GameEventRandomLog extends GameEvent { + + public final String message; + + public GameEventRandomLog(String message) { + this.message = message; + } + + @Override + public T visit(IGameEventVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java index 1566cfef093..8c579f6805e 100644 --- a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java +++ b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java @@ -38,6 +38,7 @@ public interface IGameEventVisitor { T visit(GameEventPlayerPoisoned event); T visit(GameEventPlayerPriority event); T visit(GameEventPlayerStatsChanged event); + T visit(GameEventRandomLog event); T visit(GameEventRollDie event); T visit(GameEventTokenStateUpdate event); T visit(GameEventScry event); @@ -90,6 +91,7 @@ public interface IGameEventVisitor { public T visit(GameEventPlayerPoisoned event) { return null; } public T visit(GameEventPlayerPriority event) { return null; } public T visit(GameEventPlayerStatsChanged event) { return null; } + public T visit(GameEventRandomLog event) { return null; } public T visit(GameEventRollDie event) { return null; } public T visit(GameEventTokenStateUpdate event) { return null; } public T visit(GameEventScry event) { return null; } diff --git a/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java b/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java index 70f890b7f8e..fbc4c842ef2 100644 --- a/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java +++ b/forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java @@ -67,6 +67,7 @@ public class TargetRestrictions { private boolean withSameCardType = false; private boolean singleTarget = false; private boolean randomTarget = false; + private boolean randomNumTargets = false; // How many can be targeted? private String minTargets; @@ -109,6 +110,7 @@ public class TargetRestrictions { this.withSameCardType = target.isWithSameCardType(); this.singleTarget = target.isSingleTarget(); this.randomTarget = target.isRandomTarget(); + this.randomNumTargets = target.isRandomNumTargets(); } /** @@ -696,6 +698,20 @@ public class TargetRestrictions { this.randomTarget = random; } + /** + * @return the randomNumTargets + */ + public boolean isRandomNumTargets() { + return randomNumTargets; + } + + /** + * @param randomNumTgts the randomNumTarget to set + */ + public void setRandomNumTargets(boolean randomNumTgts) { + this.randomNumTargets = randomNumTgts; + } + /** * @return the differentCMC */ diff --git a/forge-game/src/main/java/forge/util/MessageUtil.java b/forge-game/src/main/java/forge/util/MessageUtil.java index a73c34179e7..243cc38fc38 100644 --- a/forge-game/src/main/java/forge/util/MessageUtil.java +++ b/forge-game/src/main/java/forge/util/MessageUtil.java @@ -36,20 +36,32 @@ public class MessageUtil { switch(sa.getApi()) { case ChooseDirection: return value; + case ChooseColor: + return sa.hasParam("Random") + ? Localizer.getInstance().getMessage("lblRandomColorChosen", value) + : Localizer.getInstance().getMessage("lblPlayerPickedChosen", choser, value); case ChooseNumber: if (sa.hasParam("SecretlyChoose")) { return value; } return sa.hasParam("Random") - ? Localizer.getInstance().getMessage("lblPlayerRandomChosenNumberIs", mayBeYou(player, target), value) - : Localizer.getInstance().getMessage("lblPlayerChoosesNumberIs", mayBeYou(player, target), value); + ? Localizer.getInstance().getMessage("lblPlayerRandomChosenNumberIs", + mayBeYou(player, target), value) + : Localizer.getInstance().getMessage("lblPlayerChoosesNumberIs", + mayBeYou(player, target), value); case ChooseType: - return Localizer.getInstance().getMessage("lblPlayerChooseValueOfEffectOfCard", choser, value, CardTranslation.getTranslatedName(sa.getHostCard().getName())); + return sa.hasParam("AtRandom") + ? Localizer.getInstance().getMessage("lblRandomTypeChosen", value) + : Localizer.getInstance().getMessage("lblPlayerPickedChosen", choser, value); case FlipACoin: String flipper = StringUtils.capitalize(mayBeYou(player, target)); return sa.hasParam("NoCall") ? Localizer.getInstance().getMessage("lblPlayerFlipComesUpValue", Lang.getInstance().getPossesive(flipper), value) : Localizer.getInstance().getMessage("lblPlayerActionFlip", flipper, Lang.joinVerb(flipper, value)); + case GenericChoice: + if (sa.hasParam("ShowChoice") && sa.getParam("ShowChoice").equals("Description")) { + return value; + } case Protection: return Localizer.getInstance().getMessage("lblPlayerChooseValue", choser, value); case RollDice: diff --git a/forge-gui/res/cardsfolder/a/aswan_jaguar.txt b/forge-gui/res/cardsfolder/a/aswan_jaguar.txt new file mode 100644 index 00000000000..d249d923823 --- /dev/null +++ b/forge-gui/res/cardsfolder/a/aswan_jaguar.txt @@ -0,0 +1,8 @@ +Name:Aswan Jaguar +ManaCost:1 G G +Types:Creature Cat +PT:2/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChooseCT | TriggerDescription$ When CARDNAME comes into play, choose a random creature type from those in target opponent's deck. +SVar:TrigChooseCT:DB$ ChooseType | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | IsCurse$ True | AtRandom$ True | Type$ CreatureInTargetedDeck +A:AB$ Destroy | Cost$ G G T | ValidTgts$ Creature.ChosenType | NoRegen$ True | TgtPrompt$ Select target creature with the chosen type | SpellDescription$ Destroy target creature with the chosen type. It can't be regenerated. +Oracle:When Aswan Jaguar comes into play, choose a random creature type from those in target opponent's deck.\n{G}{G}, {T}: Destroy target creature with the chosen type. It can't be regenerated. diff --git a/forge-gui/res/cardsfolder/c/call_from_the_grave.txt b/forge-gui/res/cardsfolder/c/call_from_the_grave.txt new file mode 100644 index 00000000000..e3d93038d31 --- /dev/null +++ b/forge-gui/res/cardsfolder/c/call_from_the_grave.txt @@ -0,0 +1,8 @@ +Name:Call from the Grave +ManaCost:2 B +Types:Sorcery +A:SP$ ChangeZone | ChangeType$ Creature | ChangeNum$ 1 | Hidden$ True | Origin$ Graveyard | Destination$ Battlefield | AtRandom$ True | GainControl$ True | RememberChanged$ True | SubAbility$ DBDealDamage | StackDescription$ SpellDescription | SpellDescription$ Put a random creature from a random graveyard into play under your control. Call from the Grave deals to you an amount of damage equal to that creature's casting cost. +SVar:DBDealDamage:DB$ DealDamage | NumDmg$ X | Defined$ You | SubAbility$ DBCleanup | StackDescription$ None +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Remembered$CardManaCost +Oracle:Put a random creature from a random graveyard into play under your control. Call from the Grave deals to you an amount of damage equal to that creature's casting cost. diff --git a/forge-gui/res/cardsfolder/f/faerie_dragon.txt b/forge-gui/res/cardsfolder/f/faerie_dragon.txt new file mode 100644 index 00000000000..f1411735f94 --- /dev/null +++ b/forge-gui/res/cardsfolder/f/faerie_dragon.txt @@ -0,0 +1,56 @@ +Name:Faerie Dragon +ManaCost:2 G G +Types:Creature Dragon +PT:1/3 +K:Flying +A:AB$ GenericChoice | Cost$ 1 G G | AtRandom$ True | ShowChoice$ Description | Choices$ Berserk,Twiddle,BloodLust,Green,White,Red,Damage3,Flying,P3P3,Banding,Black,Blue,NoRegen,LilSneak,M2M0,ToHand,Damage1,Nerf,Exile,Orcish | StackDescription$ SpellDescription | SpellDescription$ Perform a random action. +SVar:Berserk:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | RememberChosen$ True | SubAbility$ DBPump1 | SpellDescription$ A creature chosen at random gains trample and gets +X/+0 until end of turn, where X is its power. At the beginning of the next end step, destroy that creature if it attacked this turn. +SVar:DBPump1:DB$ Pump | Defined$ Remembered | KW$ Trample | NumAtt$ X1 | SubAbility$ DBDelayedTrigger1 +SVar:DBDelayedTrigger1:DB$ DelayedTrigger | RememberObjects$ Remembered | Mode$ Phase | Phase$ End of Turn | Execute$ TrigDestroy1 | SubAbility$ DBCleanup | TriggerDescription$ At the beginning of the next end step, destroy that creature if it attacked this turn. +SVar:TrigDestroy1:DB$ Destroy | Defined$ DelayTriggerRemembered | ConditionDefined$ DelayTriggerRemembered | ConditionPresent$ Card.attackedThisTurn +SVar:X1:Remembered$CardPower +SVar:DBCleanup:DB$ Cleanup | Log$ True | ClearRemembered$ True | ClearChosenCard$ True +SVar:Twiddle:DB$ ChooseCard | Choices$ Artifact,Creature,Land | AtRandom$ True | SubAbility$ DBTapOrUntap2 | SpellDescription$ You may tap or untap an artifact, creature, or land chosen at random. +SVar:DBTapOrUntap2:DB$ TapOrUntap | Defined$ ChosenCard | SubAbility$ DBCleanup +SVar:BloodLust:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | RememberChosen$ True | SubAbility$ DBPump3 | SpellDescription$ If a creature chosen at random has toughness 5 or greater, it gets +4/-4 until end of turn. Otherwise, it gets +4/-X until end of turn, where X is its toughness minus 1. +SVar:DBPump3:DB$ Pump | Defined$ Remembered | NumAtt$ 4 | NumDef$ -X3 | SubAbility$ DBCleanup +SVar:X3:Count$Compare T3 GE4.4.T3 +SVar:T3:Remembered$CardToughness/Minus.1 +SVar:Green:DB$ ChooseCard | Choices$ Permanent | IncludeSpellsOnStack$ True | AtRandom$ True | LockInText$ True | SubAbility$ DBAnimateG | SpellDescription$ A spell or permanent chosen at random becomes green. (Mana symbols on that permanent remain unchanged.) +SVar:DBAnimateG:DB$ Animate | Defined$ ChosenCard | Colors$ Green | OverwriteColors$ True | Duration$ Permanent | LockInText$ True | SubAbility$ DBCleanup +SVar:White:DB$ ChooseCard | Choices$ Permanent | IncludeSpellsOnStack$ True | AtRandom$ True | LockInText$ True | SubAbility$ DBAnimateW | SpellDescription$ A spell or permanent chosen at random becomes white. (Mana symbols on that permanent remain unchanged.) +SVar:DBAnimateW:DB$ Animate | Defined$ ChosenCard | Colors$ White | OverwriteColors$ True | Duration$ Permanent | LockInText$ True | SubAbility$ DBCleanup +SVar:Red:DB$ ChooseCard | Choices$ Permanent | IncludeSpellsOnStack$ True | AtRandom$ True | LockInText$ True | SubAbility$ DBAnimateR | SpellDescription$ A spell or permanent chosen at random becomes red. (Mana symbols on that permanent remain unchanged.) +SVar:DBAnimateR:DB$ Animate | Defined$ ChosenCard | Colors$ Red | OverwriteColors$ True | Duration$ Permanent | LockInText$ True | SubAbility$ DBCleanup +SVar:Damage3:DB$ ChooseEntity | Random$ True | CardChoices$ Creature | PlayerChoices$ Player | RememberChosen$ True | SubAbility$ DBDamage3 | SpellDescription$ CARDNAME deals 3 damage to a creature or player chosen at random. +SVar:DBDamage3:DB$ DealDamage | Defined$ Remembered | NumDmg$ 3 | SubAbility$ DBCleanup +SVar:Flying:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | SubAbility$ DBPump8 | SpellDescription$ A creature chosen at random gains flying until end of turn. +SVar:DBPump8:DB$ Pump | Defined$ ChosenCard | KW$ Flying | SubAbility$ DBCleanup +SVar:P3P3:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | SubAbility$ DBPump9 | SpellDescription$ A creature chosen at random gets +3/+3 until end of turn. +SVar:DBPump9:DB$ Pump | Defined$ ChosenCard | NumAtt$ 3 | NumDef$ 3 | SubAbility$ DBCleanup +SVar:Banding:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | SubAbility$ DBPump10 | SpellDescription$ A creature chosen at random gains banding until end of turn. (Any creatures with banding, and up to one without, can attack in a band. Bands are blocked as a group. If any creatures with banding a player controls are blocking or being blocked by a creature, that player divides that creature's combat damage, not its controller, among any of the creatures it's being blocked by or is blocking.) +SVar:DBPump10:DB$ Pump | Defined$ ChosenCard | KW$ Banding | SubAbility$ DBCleanup +SVar:Black:DB$ ChooseCard | Choices$ Permanent | IncludeSpellsOnStack$ True | AtRandom$ True | LockInText$ True | SubAbility$ DBAnimateB | SpellDescription$ A spell or permanent chosen at random becomes black. (Mana symbols on that permanent remain unchanged.) +SVar:DBAnimateB:DB$ Animate | Defined$ ChosenCard | Colors$ Black | OverwriteColors$ True | Duration$ Permanent | LockInText$ True | SubAbility$ DBCleanup +SVar:Blue:DB$ ChooseCard | Choices$ Permanent | IncludeSpellsOnStack$ True | AtRandom$ True | LockInText$ True | SubAbility$ DBAnimateU | SpellDescription$ A spell or permanent chosen at random becomes blue. (Mana symbols on that permanent remain unchanged.) +SVar:DBAnimateU:DB$ Animate | Defined$ ChosenCard | Colors$ Blue | OverwriteColors$ True | Duration$ Permanent | LockInText$ True | SubAbility$ DBCleanup +SVar:NoRegen:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | SubAbility$ DBPump13 | SpellDescription$ A creature chosen at random can't be regenerated this turn. +SVar:DBPump13:DB$ Pump | Defined$ ChosenCard | KW$ HIDDEN CARDNAME can't be regenerated. | SubAbility$ DBCleanup +SVar:LilSneak:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | SubAbility$ DBPump14 | RememberChosen$ True | SpellDescription$ If a creature chosen at random has power 2 or less, it is unblockable this turn. +SVar:DBPump14:DB$ Pump | ConditionDefined$ Remembered | ConditionPresent$ Card.powerLE2 | Defined$ Remembered | KW$ HIDDEN Unblockable | SubAbility$ DBCleanup +SVar:M2M0:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | SubAbility$ DBPump15 | SpellDescription$ A creature chosen at random gets -2/-0 until end of turn. +SVar:DBPump15:DB$ Pump | Defined$ ChosenCard | NumAtt$ -2 | SubAbility$ DBCleanup +SVar:ToHand:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | SubAbility$ DBChangeZone16 | SpellDescription$ Return a creature chosen at random to its owner's hand. +SVar:DBChangeZone16:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBCleanup +SVar:Damage1:DB$ ChooseEntity | Random$ True | CardChoices$ Creature | PlayerChoices$ Player | RememberChosen$ True | SubAbility$ DBDamage1 | SpellDescription$ CARDNAME deals 1 damage to a creature or player chosen at random. +SVar:DBDamage1:DB$ DealDamage | Defined$ Remembered | NumDmg$ 1 | SubAbility$ DBCleanup +SVar:Nerf:DB$ ChooseCard | Choices$ Creature.Other | AtRandom$ True | SubAbility$ DBAnimate18 | SpellDescription$ A creature other than CARDNAME chosen at random becomes 0/2 until end of turn. +SVar:DBAnimate18:DB$ Animate | Defined$ ChosenCard | Power$ 0 | Toughness$ 2 | SubAbility$ DBCleanup +SVar:Exile:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | SubAbility$ DBChangeZone19 | SpellDescription$ Exile a creature chosen at random. Its controller gains life equal to its power. +SVar:DBChangeZone19:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Exile | RememberLKI$ True | SubAbility$ DBGainLife19 +SVar:DBGainLife19:DB$ GainLife | Defined$ RememberedController | LifeAmount$ X19 | SubAbility$ DBCleanup +SVar:X19:RememberedLKI$CardPower +SVar:Orcish:DB$ ChooseCard | Choices$ Creature | Amount$ Random | AtRandom$ True | SubAbility$ DBPutCounter20 | SpellDescription$ Randomly distribute X -0/-1 counters among a random number of creatures chosen at random, where X is the number of creatures in play. +SVar:DBPutCounter20:DB$ PutCounter | Defined$ ChosenCard | CounterType$ M0M1 | CounterNum$ X20 | DividedRandomly$ True | SubAbility$ DBCleanup +SVar:X20:Count$Valid Creature +Oracle:Flying\n{1}{G}{G}: Perform a random action. diff --git a/forge-gui/res/cardsfolder/g/gem_bazaar.txt b/forge-gui/res/cardsfolder/g/gem_bazaar.txt new file mode 100644 index 00000000000..f80b553a20f --- /dev/null +++ b/forge-gui/res/cardsfolder/g/gem_bazaar.txt @@ -0,0 +1,7 @@ +Name:Gem Bazaar +ManaCost:no cost +Types:Land +K:ETBReplacement:Other:ChooseColor +SVar:ChooseColor:DB$ ChooseColor | Random$ True | SpellDescription$ As CARDNAME enters the battlefield, choose a color at random. +A:AB$ Mana | Cost$ T | Produced$ Chosen | SubAbility$ ChooseColor | SpellDescription$ Add one mana of the chosen color. Then choose a color at random. +Oracle:As Gem Bazaar enters the battlefield, choose a color at random.\n{T}: Add one mana of the chosen color. Then choose a color at random. diff --git a/forge-gui/res/cardsfolder/g/goblin_polka_band.txt b/forge-gui/res/cardsfolder/g/goblin_polka_band.txt new file mode 100644 index 00000000000..4db2e326794 --- /dev/null +++ b/forge-gui/res/cardsfolder/g/goblin_polka_band.txt @@ -0,0 +1,10 @@ +Name:Goblin Polka Band +ManaCost:R R +Types:Creature Goblin +PT:1/1 +A:AB$ Tap | Announce$ TgtNum | AnnounceTitle$ any number of creatures to target | Cost$ X 2 T | XColor$ R | CostDesc$ {2}, {T}: | ValidTgts$ Creature.untapped | TargetMin$ TgtNum | TargetMax$ TgtNum | TargetsAtRandom$ True | RememberTargets$ True | SubAbility$ GoblinHangover | SpellDescription$ Tap any number of random target creatures. Goblins tapped in this way do not untap during their controllers' next untap phases. This ability costs {R} more to activate for each target. +SVar:GoblinHangover:DB$ PumpAll | ValidCards$ Goblin.IsRemembered | KW$ HIDDEN This card doesn't untap during your next untap step. | Duration$ Permanent +SVar:TgtNum:Number$0 +SVar:X:SVar$TgtNum +SVar:Y:Count$xPaid +Oracle:{2}, {T}: Tap any number of random target creatures. Goblins tapped in this way do not untap during their controllers' next untap phases. This ability costs {R} more to activate for each target. diff --git a/forge-gui/res/cardsfolder/g/power_struggle.txt b/forge-gui/res/cardsfolder/g/power_struggle.txt new file mode 100644 index 00000000000..7b662e546c0 --- /dev/null +++ b/forge-gui/res/cardsfolder/g/power_struggle.txt @@ -0,0 +1,7 @@ +Name:Power Struggle +ManaCost:2 U U U +Types:Enchantment +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerController$ TriggeredPlayer | TriggerDescription$ At the beginning of each player's upkeep, that player exchanges control of random target artifact, creature or land he or she controls, for control of random target permanent of the same type that a random opponent controls. +SVar:TrigPump:DB$ Pump | TargetsWithDefinedController$ TriggeredPlayer | ValidTgts$ Artifact,Creature,Land | TargetsAtRandom$ True | SubAbility$ DBExchangeControl +SVar:DBExchangeControl:DB$ ExchangeControl | Defined$ ParentTarget | ValidTgts$ Artifact,Creature,Land | TargetsWithDefinedController$ Player.OpponentOf TriggeredPlayer | TargetsWithSharedCardType$ ParentTarget | TargetsAtRandom$ True +Oracle:At the beginning of each player's upkeep, that player exchanges control of random target artifact, creature or land he or she controls, for control of random target permanent of the same type that a random opponent controls. diff --git a/forge-gui/res/cardsfolder/n/necropolis_of_azar.txt b/forge-gui/res/cardsfolder/n/necropolis_of_azar.txt new file mode 100644 index 00000000000..585a1c25ca6 --- /dev/null +++ b/forge-gui/res/cardsfolder/n/necropolis_of_azar.txt @@ -0,0 +1,10 @@ +Name:Necropolis of Azar +ManaCost:2 B B +Types:Enchantment +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.nonBlack | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever a nonblack creature dies, put a husk counter on CARDNAME. +SVar:TrigPutCounter:DB$ PutCounter | CounterType$ HUSK +A:AB$ Token | Cost$ 5 SubCounter<1/HUSK> | TokenScript$ spawn_of_azar | TokenPower$ X | TokenToughness$ Y | SpellDescription$ Create an X/Y black Spawn creature token with swampwalk named Spawn of Azar, where X and Y are numbers chosen at random from 1 to 3. +SVar:X:Count$Random.1.3 +SVar:Y:Count$Random.1.3 +DeckHas:Ability$Counters & Ability$Token +Oracle:Whenever a nonblack creature dies, put a husk counter on Necropolis of Azar.\n{5}, Remove a husk counter from Necropolis of Azar: Create an X/Y black Spawn creature token with swampwalk named Spawn of Azar, where X and Y are numbers chosen at random from 1 to 3. diff --git a/forge-gui/res/cardsfolder/o/orcish_catapult.txt b/forge-gui/res/cardsfolder/o/orcish_catapult.txt new file mode 100644 index 00000000000..b0efe834bad --- /dev/null +++ b/forge-gui/res/cardsfolder/o/orcish_catapult.txt @@ -0,0 +1,6 @@ +Name:Orcish Catapult +ManaCost:X R R +Types:Instant +A:SP$ PutCounter | ValidTgts$ Creature | TargetsAtRandom$ True | CounterType$ M0M1 | CounterNum$ X | RandomNumTargets$ True | TargetMin$ 0 | TargetMax$ X | DividedRandomly$ True | SpellDescription$ Randomly distribute X -0/-1 counters among a random number of random target creatures. +SVar:X:Count$xPaid +Oracle:Randomly distribute X -0/-1 counters among a random number of random target creatures. diff --git a/forge-gui/res/cardsfolder/p/pandoras_box.txt b/forge-gui/res/cardsfolder/p/pandoras_box.txt new file mode 100644 index 00000000000..4474b1ab52f --- /dev/null +++ b/forge-gui/res/cardsfolder/p/pandoras_box.txt @@ -0,0 +1,9 @@ +Name:Pandora's Box +ManaCost:5 +Types:Artifact +A:AB$ ChooseCard | Cost$ 3 T | Choices$ Creature | AtRandom$ True | AllCards$ True | SubAbility$ DBRepeatEach | StackDescription$ SpellDescription | SpellDescription$ Choose a creature card at random from all players' decklists. Each player flips a coin. Each player whose coin comes up heads creates a token that's a copy of that card. +SVar:DBRepeatEach:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBFlip | SubAbility$ DBCleanup +SVar:DBFlip:DB$ FlipACoin | Flipper$ Remembered | NoCall$ True | HeadsSubAbility$ DBCopyPermanent +SVar:DBCopyPermanent:DB$ CopyPermanent | Defined$ ChosenCard | Controller$ Remembered +SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True +Oracle:{3}, {T}: Choose a creature card at random from all players' decklists. Each player flips a coin. Each player whose coin comes up heads creates a token that's a copy of that card. diff --git a/forge-gui/res/cardsfolder/p/prismatic_dragon.txt b/forge-gui/res/cardsfolder/p/prismatic_dragon.txt new file mode 100644 index 00000000000..c9c74376029 --- /dev/null +++ b/forge-gui/res/cardsfolder/p/prismatic_dragon.txt @@ -0,0 +1,11 @@ +Name:Prismatic Dragon +ManaCost:2 W W +Types:Creature Dragon +PT:2/3 +K:Flying +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigChooseColor | TriggerDescription$ At the beginning of your upkeep, CARDNAME becomes a color chosen at random. (This effect lasts indefinitely.) +SVar:TrigChooseColor:DB$ ChooseColor | Random$ True | SubAbility$ DBAnimate +SVar:DBAnimate:DB$ Animate | Colors$ ChosenColor | OverwriteColors$ True | Duration$ Permanent | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearChosenColor$ True +A:AB$ ChooseColor | Cost$ 2 | Random$ True | SubAbility$ DBAnimate | StackDescription$ SpellDescription | SpellDescription$ CARDNAME becomes a color chosen at random. (This effect lasts indefinitely.) +Oracle:Flying\nAt the beginning of your upkeep, Prismatic Dragon becomes a color chosen at random. (This effect lasts indefinitely.)\n{2}: Prismatic Dragon becomes a color chosen at random. (This effect lasts indefinitely.) diff --git a/forge-gui/res/cardsfolder/r/rainbow_knights.txt b/forge-gui/res/cardsfolder/r/rainbow_knights.txt new file mode 100644 index 00000000000..06acaf18cd5 --- /dev/null +++ b/forge-gui/res/cardsfolder/r/rainbow_knights.txt @@ -0,0 +1,11 @@ +Name:Rainbow Knights +ManaCost:W W +Types:Creature Human Knight +PT:2/1 +K:ETBReplacement:Other:ChooseColor +SVar:ChooseColor:DB$ ChooseColor | Random$ True | SpellDescription$ As CARDNAME enters the battlefield, it gains protection from a color chosen at random. (This effect lasts indefinitely.) +S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ Protection:Card.ChosenColor:Protection from chosenColor | Secondary$ True +A:AB$ Pump | Cost$ 1 | KW$ First strike | SpellDescription$ CARDNAME gains first strike until end of turn. +A:AB$ Pump | Cost$ W W | NumAtt$ X | StackDescription$ SpellDescription | SpellDescription$ CARDNAME gets +X/+0 until end of turn, where X is a number chosen randomly from 0 to 2. | +SVar:X:Count$Random.0.2 +Oracle:As Rainbow Knights enters the battlefield, it gains protection from a color chosen at random. (This effect lasts indefinitely.)\n{1}: Rainbow Knights gains first strike until end of turn.\n{W}{W}: Rainbow Knights gets +X/+0 until end of turn, where X is a number chosen randomly from 0 to 2. diff --git a/forge-gui/res/cardsfolder/w/whimsy.txt b/forge-gui/res/cardsfolder/w/whimsy.txt new file mode 100644 index 00000000000..e37a06cbc71 --- /dev/null +++ b/forge-gui/res/cardsfolder/w/whimsy.txt @@ -0,0 +1,47 @@ +Name:Whimsy +ManaCost:X U U +Types:Sorcery +A:SP$ Repeat | MaxRepeat$ X | RepeatSubAbility$ DBGenericChoice | StackDescription$ SpellDescription | SpellDescription$ Perform X random actions. +SVar:DBGenericChoice:DB$ GenericChoice | ShowChoice$ Description | AtRandom$ True | Choices$ ToHand,Untap,Tap,Damage,Draw3,DestroyGain,DestroyAE,Gain3,Anoint,DestroyCL,Mill2,Wasp,Nevinyrral,Suleiman,Pandora,Discard,Fog,Sindbad +SVar:ToHand:DB$ ChooseCard | Choices$ Permanent.unenchanted | AtRandom$ True | SubAbility$ DBChangeZone3 | SpellDescription$ Return a permanent that isn't enchanted chosen at random to its owner's hand. +SVar:DBChangeZone3:DB$ ChangeZone | Defined$ ChosenCard | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBCleanup +SVar:Untap:DB$ ChooseCard | Choices$ Artifact.tapped,Creature.tapped,Land.tapped | AtRandom$ True | SubAbility$ DBUntap4 | SpellDescription$ Untap an artifact, creature or land chosen at random. +SVar:DBUntap4:DB$ Untap | Defined$ ChosenCard | SubAbility$ DBCleanup +SVar:Tap:DB$ ChooseCard | Choices$ Artifact.untapped,Creature.untapped,Land.untapped | AtRandom$ True | SubAbility$ DBTap5 | SpellDescription$ Tap an artifact, creature or land chosen at random. +SVar:DBTap5:DB$ Tap | Defined$ ChosenCard | SubAbility$ DBCleanup +SVar:Damage:DB$ ChooseEntity | Random$ True | CardChoices$ Creature | PlayerChoices$ Player | RememberChosen$ True | SubAbility$ DBDamage6 | SpellDescription$ CARDNAME deals 4 damage to a creature or player chosen at random. +SVar:DBDamage6:DB$ DealDamage | Defined$ Remembered | NumDmg$ 4 | SubAbility$ DBCleanup +SVar:Draw3:DB$ ChoosePlayer | Choices$ Player | Random$ True | SubAbility$ DBDraw7 | SpellDescription$ A player chosen at random draws three cards. +SVar:DBDraw7:DB$ Draw | NumCards$ 3 | Defined$ ChosenPlayer | SubAbility$ DBCleanup +SVar:DestroyGain:DB$ ChooseCard | Choices$ Artifact | AtRandom$ True | SubAbility$ DBDestroy8 | SpellDescription$ Destroy an artifact chosen at random. It can't be regenerated. That artifact's controller gains life equal to its converted mana cost. +SVar:DBDestroy8:DB$ Destroy | Defined$ ChosenCard | NoRegen$ True | RememberDestroyed$ True | SubAbility$ DBGainLife8 +SVar:DBGainLife8:DB$ GainLife | Defined$ RememberedController | LifeAmount$ X8 | SubAbility$ DBCleanup +SVar:X8:Remembered$CardManaCost +SVar:DestroyAE:DB$ ChooseCard | Choices$ Artifact,Enchantment | AtRandom$ True | SubAbility$ DBDestroy9 | SpellDescription$ Destroy an artifact or enchantment chosen at random. +SVar:DBDestroy9:DB$ Destroy | Defined$ ChosenCard | SubAbility$ DBCleanup +SVar:Gain3:DB$ ChoosePlayer | Choices$ Player | Random$ True | SubAbility$ DBGainLife10 | SpellDescription$ A player chosen at random gains 3 life. +SVar:DBGainLife10:DB$ GainLife | Defined$ ChosenPlayer | LifeAmount$ 3 | SubAbility$ DBCleanup +SVar:Anoint:DB$ ChooseEntity | Random$ True | CardChoices$ Creature | PlayerChoices$ Player | RememberChosen$ True | SubAbility$ DBPreventDamage11 | SpellDescription$ Prevent the next 3 damage that would be dealt to a creature or player chosen at random this turn. +SVar:DBPreventDamage11:DB$ PreventDamage | Defined$ Remembered | Amount$ 3 | SubAbility$ DBCleanup +SVar:DestroyCL:DB$ ChooseCard | Choices$ Creature,Land | AtRandom$ True | SubAbility$ DBDestroy12 | SpellDescription$ Destroy a creature or land chosen at random. It can't be regenerated. +SVar:DBDestroy12:DB$ Destroy | Defined$ ChosenCard | NoRegen$ True | SubAbility$ DBCleanup +SVar:Mill2:DB$ ChoosePlayer | Choices$ Player | Random$ True | SubAbility$ DBMill13 | SpellDescription$ A player chosen at random mills two cards. +SVar:DBMill13:DB$ Mill | Defined$ ChosenPlayer | NumCards$ 2 | SubAbility$ DBCleanup +SVar:Wasp:DB$ Token | TokenScript$ wasp | LockInText$ True | SpellDescription$ Create a 1/1 colorless Insect artifact creature token with flying named Wasp. +SVar:Nevinyrral:DB$ DestroyAll | ValidCards$ Artifact,Creature,Enchantment | SpellDescription$ Destroy all artifacts, creatures and enchantments. +SVar:Suleiman:DB$ FlipACoin | WinSubAbility$ DBToken | LoseSubAbility$ DBDamage | LockInText$ True | SpellDescription$ Flip a coin. If you win the flip, create a 5/5 colorless Djinn artifact creature token with flying. If you lose the flip, CARDNAME deals 5 damage to you. +SVar:DBToken:DB$ Token | LockInText$ True | TokenScript$ c_5_5_a_djinn_flying +SVar:DBDamage:DB$ DealDamage | Defined$ You | NumDmg$ 5 +SVar:Pandora:DB$ ChooseCard | Choices$ Creature | AtRandom$ True | AllCards$ True | SubAbility$ DBRepeatEach17 | SpellDescription$ Choose a creature card at random from all players' decklists. Each player flips a coin. Each player whose coin comes up heads creates a token that's a copy of that card. +SVar:DBRepeatEach17:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBFlip17 | SubAbility$ DBCleanup +SVar:DBFlip17:DB$ FlipACoin | Flipper$ Remembered | NoCall$ True | HeadsSubAbility$ DBCopyPermanent17 +SVar:DBCopyPermanent17:DB$ CopyPermanent | Defined$ ChosenCard | Controller$ Remembered +SVar:Discard:DB$ ChoosePlayer | Choices$ Player | Random$ True | SubAbility$ DBDiscard18 | SpellDescription$ A player chosen at random discards a card. +SVar:DBDiscard18:DB$ Discard | Defined$ ChosenPlayer | NumCards$ 1 | Mode$ TgtChoose | SubAbility$ DBCleanup +SVar:Fog:DB$ Fog | SpellDescription$ Prevent all combat damage that would be dealt this turn. +SVar:Sindbad:DB$ Draw | NumCards$ 1 | Reveal$ All | RememberDrawn$ True | SubAbility$ DBDiscard20 | SpellDescription$ Draw a card and reveal it. If it isn't a land card, discard it. +SVar:DBDiscard20:DB$ Discard | Mode$ Defined | Defined$ You | DefinedCards$ Remembered | ConditionDefined$ Remembered | ConditionPresent$ Card.nonLand | ConditionCompare$ EQ1 | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | Log$ True | ClearRemembered$ True | ClearChosenCard$ True | ClearChosenPlayer$ True +SVar:X:Count$xPaid +DeckHas:Ability$LifeGain & Ability$Token & Ability$Discard +Oracle:Perform X random actions. diff --git a/forge-gui/res/editions/Astral Cards.txt b/forge-gui/res/editions/Astral Cards.txt new file mode 100644 index 00000000000..e1c0adb2e4a --- /dev/null +++ b/forge-gui/res/editions/Astral Cards.txt @@ -0,0 +1,19 @@ +[metadata] +Code=PAST +Date=1997-04-01 +Name=Astral Cards +Type=Funny + +[cards] +1 C Aswan Jaguar @Pat Lewis +2 C Call from the Grave @Quinton Hoover +3 C Faerie Dragon @NéNé Thomas +4 C Goblin Polka Band @Quinton Hoover +5 C Necropolis of Azar @Rob Alexander +6 C Orcish Catapult @Melissa A. Benson +7 C Power Struggle @Mark Tedin +8 C Prismatic Dragon @Amy Weber +9 C Rainbow Knights @Douglas Shuler +10 C Whimsy @Anson Maddocks +11 C Pandora's Box @Amy Weber +12 C Gem Bazaar @Liz Danforth diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index fa5276fcd3d..0ee4d3d67e9 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -1449,6 +1449,7 @@ lblTriggered=löst aus lblActivated=aktiviert lblLogPlayerActionObjectWitchTarget={0} {1} {2} mit Ziel {3} lblLogPlayerActionObject={0} {1} {2} +lblLogRandomMode={0}''s random mode: {1} lblLogPlayerChosenModeForCard=[0} wählte {1} für {2} lblLogPlayerHasRestoredControlThemself={0} hat die eigene Kontrolle zurück lblLogPlayerControlledTargetPlayer={0} wird kontrolliert durch {1} @@ -1846,6 +1847,11 @@ lblChooseOpponent=Wähle einen Gegner lblReveals=zeigt offen vor lblWinsClash=gewinnt Fehde lblLosesClash=verliert Fehde +#CleanUpEffect.java +lblChosenCard={0}''s chosen card: {1} +lblChosenMultiCard={0}''s chosen cards: {1} +lblChosenPlayer={0}''s chosen player: {1} +lblNoValidChoice={0} found no valid choices. #CloneEffect.java lblDoYouWantCopy=Möchtest du {0} kopieren? #ControlExchangeEffect.java @@ -2129,6 +2135,8 @@ lblGameplayResults=Spielergebnis lblResultIs=Ergebnis: {0} lblPlayerRandomChosenNumberIs=Zufällige Zahl von {0} bis {1} lblPlayerChoosesNumberIs={0} wählt Zahl: {1} +lblRandomColorChosen=Randomly chosen color: {0} +lblRandomTypeChosen=Randomly chosen type: {0} lblPlayerChooseValueOfEffectOfCard={0} wählt {1} als Effekt von {2} lblPlayerFlipComesUpValue={0}-Wurf zeigt {1} lblPlayerActionFlip={0} {1} den Wurf diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 0a0a028a233..106e15bfc0e 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -1451,6 +1451,7 @@ lblTriggered=triggered lblActivated=activated lblLogPlayerActionObjectWitchTarget={0} {1} {2} targeting {3} lblLogPlayerActionObject={0} {1} {2} +lblLogRandomMode={0}''s random mode: {1} lblLogPlayerChosenModeForCard={0} has chosen {1} for {2}. lblLogPlayerHasRestoredControlThemself={0} has restored control over themself lblLogPlayerControlledTargetPlayer={0} is controlled by {1} @@ -1847,6 +1848,11 @@ lblChooseOpponent=Choose a opponent lblReveals=reveals lblWinsClash=wins clash lblLosesClash=loses clash +#CleanUpEffect.java +lblChosenCard={0}''s chosen card: {1} +lblChosenMultiCard={0}''s chosen cards: {1} +lblChosenPlayer={0}''s chosen player: {1} +lblNoValidChoice={0} found no valid choices. #CloneEffect.java lblDoYouWantCopy=Do you want to copy {0}? #ControlExchangeEffect.java @@ -2128,7 +2134,9 @@ lblGameplayResults=Gameplay Results lblResultIs=Result: {0} lblPlayerRandomChosenNumberIs=Randomly chosen number for {0} is {1} lblPlayerChoosesNumberIs={0} chooses number: {1} -lblPlayerChooseValueOfEffectOfCard={0} choose {1} for effect of {2} +lblRandomColorChosen=Randomly chosen color: {0} +lblRandomTypeChosen=Randomly chosen type: {0} +lblPlayerChooseValueOfEffectOfCard={0} chose {1} for effect of {2} lblPlayerFlipComesUpValue={0} flip comes up {1} lblPlayerActionFlip={0} {1} the flip lblPlayerChooseValue={0} choose {1} diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index 991f2b8aded..00601c6b726 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -1449,6 +1449,7 @@ lblTriggered=acciona lblActivated=activa lblLogPlayerActionObjectWitchTarget={0} {1} {2} con objetivo {3} lblLogPlayerActionObject={0} {1} {2} +lblLogRandomMode={0}''s random mode: {1} lblLogPlayerChosenModeForCard={0} ha elegido {1} para {2}. lblLogPlayerHasRestoredControlThemself={0} ha restaurado el control sobre sí mismo lblLogPlayerControlledTargetPlayer={0} es controlado por {1} @@ -1845,6 +1846,11 @@ lblChooseOpponent=Elige un adversario lblReveals=muestra lblWinsClash=gana el enfrentamiento lblLosesClash=pierde el enfrentamiento +#CleanUpEffect.java +lblChosenCard={0}''s chosen card: {1} +lblChosenMultiCard={0}''s chosen cards: {1} +lblChosenPlayer={0}''s chosen player: {1} +lblNoValidChoice={0} found no valid choices. #CloneEffect.java lblDoYouWantCopy=¿Quieres copiar {0}? #ControlExchangeEffect.java @@ -2128,6 +2134,8 @@ lblGameplayResults=Resultados del Juego lblResultIs=Resultado: {0} lblPlayerRandomChosenNumberIs=El número elegido aleatoriamente para {0} es {1} lblPlayerChoosesNumberIs={0} elige el número: {1} +lblRandomColorChosen=Color aleatorio es {0} +lblRandomTypeChosen=Tipo aleatorio es {0} lblPlayerChooseValueOfEffectOfCard={0} elige {1} para el efecto de {2} lblPlayerFlipComesUpValue=El lanzamiento de {0} sale {1} lblPlayerActionFlip={0} {1} el lanzamiento diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties index 021eec01988..5975b6db69d 100644 --- a/forge-gui/res/languages/it-IT.properties +++ b/forge-gui/res/languages/it-IT.properties @@ -1449,6 +1449,7 @@ lblTriggered=ha innescato lblActivated=ha attivato lblLogPlayerActionObjectWitchTarget={0} {1} {2} con bersaglio {3} lblLogPlayerActionObject={0} {1} {2} +lblLogRandomMode={0}''s random mode: {1} lblLogPlayerChosenModeForCard={0} ha scelto {1} per {2}. lblLogPlayerHasRestoredControlThemself={0} ha ripreso il controllo di sé stesso lblLogPlayerControlledTargetPlayer={0} è controllato da {1} @@ -1844,6 +1845,11 @@ lblChooseOpponent=Scegli un avversario lblReveals=rivela lblWinsClash=vince lo scontro lblLosesClash=perde lo scontro +#CleanUpEffect.java +lblChosenCard={0}''s chosen card: {1} +lblChosenMultiCard={0}''s chosen cards: {1} +lblChosenPlayer={0}''s chosen player: {1} +lblNoValidChoice={0} found no valid choices. #CloneEffect.java lblDoYouWantCopy=Vuoi copiare {0}? #ControlExchangeEffect.java @@ -2127,6 +2133,8 @@ lblGameplayResults=Risultati lblResultIs=Risultato: {0} lblPlayerRandomChosenNumberIs=Il numero scelto a caso per {0} è {1} lblPlayerChoosesNumberIs={0} sceglie il numero: {1} +lblRandomColorChosen=Randomly chosen color: {0} +lblRandomTypeChosen=Randomly chosen type: {0} lblPlayerChooseValueOfEffectOfCard={0} ha scelto{1} per effetto di {2} lblPlayerFlipComesUpValue=Al lancio di {0} è uscita {1} lblPlayerActionFlip={0} {1} il lancio diff --git a/forge-gui/res/languages/ja-JP.properties b/forge-gui/res/languages/ja-JP.properties index 1336ab6211e..3af3c6c4420 100644 --- a/forge-gui/res/languages/ja-JP.properties +++ b/forge-gui/res/languages/ja-JP.properties @@ -1450,6 +1450,7 @@ lblTriggered=が誘発された lblActivated=を起動した lblLogPlayerActionObjectWitchTarget={0}: {3}を対象に {2} {1} lblLogPlayerActionObject={0}: {2} {1} +lblLogRandomMode={0}''s random mode: {1} lblLogPlayerChosenModeForCard={0}は {2}の {1}を選択しました。 lblLogPlayerHasRestoredControlThemself={0}は自身の制御が戻りました lblLogPlayerControlledTargetPlayer={0}は {1}によって制御されます @@ -1844,6 +1845,11 @@ lblChooseOpponent=対戦相手1人を選ぶ lblReveals=公開する lblWinsClash=が激突に勝利した lblLosesClash=が激突に負けた +#CleanUpEffect.java +lblChosenCard={0}''s chosen card: {1} +lblChosenMultiCard={0}''s chosen cards: {1} +lblChosenPlayer={0}''s chosen player: {1} +lblNoValidChoice={0} found no valid choices. #CloneEffect.java lblDoYouWantCopy={0}をコピーしますか? #ControlExchangeEffect.java @@ -2127,6 +2133,8 @@ lblGameplayResults=ゲームの結果 lblResultIs=結果:{0} lblPlayerRandomChosenNumberIs={0}のランダムに選択された数値は{1}です lblPlayerChoosesNumberIs={0}が番号を選択:{1} +lblRandomColorChosen=Randomly chosen color: {0} +lblRandomTypeChosen=Randomly chosen type: {0} lblPlayerChooseValueOfEffectOfCard={0}は{2}の効果のために{1}を選択します lblPlayerFlipComesUpValue={0}コイン投げ:{1} lblPlayerActionFlip={0}のコイン投げ:{1} diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties index cbe09835b5c..83d98afd192 100644 --- a/forge-gui/res/languages/zh-CN.properties +++ b/forge-gui/res/languages/zh-CN.properties @@ -1451,6 +1451,7 @@ lblTriggered=触发了 lblActivated=起动了 lblLogPlayerActionObjectWitchTarget={0}{1}{2}目标为{3} lblLogPlayerActionObject={0}{1}{2} +lblLogRandomMode={0}''s random mode: {1} lblLogPlayerChosenModeForCard={0}为{2}选择了模式{1}。 lblLogPlayerHasRestoredControlThemself={0}恢复了他的控制权 lblLogPlayerControlledTargetPlayer={0}控制了{1} @@ -1848,6 +1849,11 @@ lblChooseOpponent=选择一个对手 lblReveals=展示 lblWinsClash=比点赢了 lblLosesClash=比点输了 +#CleanUpEffect.java +lblChosenCard={0}''s chosen card: {1} +lblChosenMultiCard={0}''s chosen cards: {1} +lblChosenPlayer={0}''s chosen player: {1} +lblNoValidChoice={0} found no valid choices. #CloneEffect.java lblDoYouWantCopy=你想要复制{0}吗? #ControlExchangeEffect.java @@ -2129,6 +2135,8 @@ lblGameplayResults=游戏结果 lblResultIs=结果为:{0} lblPlayerRandomChosenNumberIs={0}随机选择的数字为{1} lblPlayerChoosesNumberIs={0}选择的数字为:{1} +lblRandomColorChosen=Randomly chosen color: {0} +lblRandomTypeChosen=Randomly chosen type: {0} lblPlayerChooseValueOfEffectOfCard={0}选择{2}对{1}生效 lblPlayerFlipComesUpValue={0}掷到了{1} lblPlayerActionFlip={0}{1}了骰子 diff --git a/forge-gui/res/tokenscripts/spawn_of_azar.txt b/forge-gui/res/tokenscripts/spawn_of_azar.txt new file mode 100644 index 00000000000..67892705eb6 --- /dev/null +++ b/forge-gui/res/tokenscripts/spawn_of_azar.txt @@ -0,0 +1,7 @@ +Name:Spawn of Azar +ManaCost:no cost +Types:Creature Spawn +Colors:black +PT:*/* +K:Swampwalk +Oracle:Swampwalk diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index 2377df5cafd..251bf43ca65 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -193,7 +193,7 @@ public class HumanPlaySpellAbility { // no worries here. The same thread must resolve, and by this moment ability will have been resolved already // Triggers haven't resolved yet ?? - if (mayChooseTargets) { + if (mayChooseTargets && !ability.hasParam("TargetsAtRandom")) { ability.clearTargets(); } if (manaTypeConversion || manaColorConversion || keywordColor) { diff --git a/forge-gui/src/main/java/forge/player/TargetSelection.java b/forge-gui/src/main/java/forge/player/TargetSelection.java index 2a58646175c..ba59f027510 100644 --- a/forge-gui/src/main/java/forge/player/TargetSelection.java +++ b/forge-gui/src/main/java/forge/player/TargetSelection.java @@ -117,9 +117,20 @@ public class TargetSelection { final boolean choiceResult; if (tgt.isRandomTarget() && numTargets == null) { - final List candidates = tgt.getAllCandidates(this.ability, true); - final GameObject choice = Aggregates.random(candidates); - return ability.getTargets().add(choice); + List candidates = tgt.getAllCandidates(this.ability, true); + List choices = new ArrayList<>(); + // currently, only cards that target randomly use a random number of targets + int top = Math.min(candidates.size(), maxTargets); // prevents choosing more targets than possible + int bot = minTargets > 0 ? minTargets : 1; // prevents randomly choosing zero targets + int num = tgt.isRandomNumTargets() ? Aggregates.randomInt(bot, top) : minTargets; + for (int i=0; i