Merge branch 'astral' into 'master'

MicroProse Astral cards!?!

See merge request core-developers/forge!5465
This commit is contained in:
Michael Kamensky
2021-10-22 12:45:15 +00:00
40 changed files with 590 additions and 64 deletions

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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<GameLogEntry> {
@@ -128,10 +113,25 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
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<GameOutcome> gamesPlayed) {
final GameOutcome outcome1 = Iterables.getFirst(gamesPlayed, null);
final HashMap<RegisteredPlayer, String> players = outcome1.getPlayerNames();

View File

@@ -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);
}

View File

@@ -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),

View File

@@ -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) {

View File

@@ -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<String> chosenColors;
List<String> 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));
}
}
Player noNotify = p;
if (sa.hasParam("Random")) {
String choice;
for (int i=0; i<cntMin; i++) {
choice = Aggregates.random(colorChoices);
colorChoices.remove(choice);
chosenColors.add(choice);
}
noNotify = null;
} else {
chosenColors = p.getController().chooseColors(prompt, sa, cntMin, cntMax, colorChoices);
}
if (chosenColors.isEmpty()) {
return;
}
card.setChosenColors(chosenColors);
p.getGame().getAction().notifyOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), Lang.joinHomogenous(chosenColors)), p);
p.getGame().getAction().notifyOfValue(sa, p, Lang.joinHomogenous(chosenColors), noNotify);
}
}
}

View File

@@ -0,0 +1,54 @@
package forge.game.ability.effects;
import com.google.common.collect.Lists;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.player.PlayerCollection;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import java.util.List;
public class ChooseEntityEffect extends SpellAbilityEffect {
@Override
protected String getStackDescription(SpellAbility sa) {
return (sa.hasParam("StackDescription") ? sa.getParam("StackDescription") :
sa.getParamOrDefault("SpellDescription", "Write a Stack/SpellDescription!"));
}
@Override
public void resolve(SpellAbility sa) {
final Card host = sa.getHostCard();
final Player activator = sa.getActivatingPlayer();
final Game game = activator.getGame();
List<GameEntity> 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);
}
}
}

View File

@@ -73,7 +73,9 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
List<SpellAbility> 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).");
}
}

View File

@@ -19,10 +19,14 @@ public class ChooseTypeEffect extends SpellAbilityEffect {
protected String getStackDescription(SpellAbility sa) {
final StringBuilder sb = new StringBuilder();
if (!sa.usesTargeting()) {
for (final Player p : getTargetPlayers(sa)) {
sb.append(p).append(" ");
sb.append(p);
}
sb.append(" chooses a type.");
} else {
sb.append("Please improve the stack description.");
}
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<String> invalidTypes = sa.hasParam("InvalidTypes") ? Arrays.asList(sa.getParam("InvalidTypes").split(",")) : new ArrayList<>();
final List<String> validTypes = new ArrayList<>();
final List<Player> 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<Player> 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 {

View File

@@ -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();
}
}

View File

@@ -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<String, Object> params = Maps.newHashMap();
params.put("CounterType", counterType);
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<Object, Integer> randomMap = Maps.newHashMap();
for (int i=0; i<counterRemain; i++) {
Card found = Aggregates.random(targets);
found.addCounter(counterType, 1, placer, sa, true, table);
if (randomMap.containsKey(found)) {
int oN = randomMap.get(found);
int nN = oN+1;
randomMap.replace(found, oN, nN);
} else {
randomMap.put(found, 1);
}
}
game.fireEvent(new GameEventRandomLog(logOutput(randomMap, card)));
} else for (final GameObject obj : tgtObjects) {
// check if the object is still in game or if it was moved
Card gameCard = null;
if (obj instanceof Card) {
@@ -461,4 +498,22 @@ public class CountersPutEffect extends SpellAbilityEffect {
sa.getActivatingPlayer().getGame().getTriggerHandler().registerDelayedTrigger(trig);
}
}
protected String logOutput(Map<Object, Integer> 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<Object, Integer> 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();
}
}

View File

@@ -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),

View File

@@ -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

View File

@@ -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> T visit(IGameEventVisitor<T> visitor) {
return visitor.visit(this);
}
}

View File

@@ -38,6 +38,7 @@ public interface IGameEventVisitor<T> {
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<T> {
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; }

View File

@@ -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
*/

View File

@@ -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:

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.)

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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}了骰子

View File

@@ -0,0 +1,7 @@
Name:Spawn of Azar
ManaCost:no cost
Types:Creature Spawn
Colors:black
PT:*/*
K:Swampwalk
Oracle:Swampwalk

View File

@@ -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) {

View File

@@ -117,9 +117,20 @@ public class TargetSelection {
final boolean choiceResult;
if (tgt.isRandomTarget() && numTargets == null) {
final List<GameEntity> candidates = tgt.getAllCandidates(this.ability, true);
final GameObject choice = Aggregates.random(candidates);
return ability.getTargets().add(choice);
List<GameEntity> candidates = tgt.getAllCandidates(this.ability, true);
List<GameEntity> 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<num; i++) {
final GameEntity choice = Aggregates.random(candidates);
if (choice != null) {
choices.add(choice);
candidates.remove(choice);
}
}
return ability.getTargets().addAll(choices);
}
else if (zones.size() == 1 && zones.get(0) == ZoneType.Stack) {
// If Zone is Stack, the choices are handled slightly differently.