mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Compare commits
38 Commits
chooseColo
...
migrate-tl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54b4f3edf1 | ||
|
|
e95673e0a2 | ||
|
|
41b39e74c3 | ||
|
|
b0d5fc5709 | ||
|
|
7b60e06551 | ||
|
|
c3869f4fba | ||
|
|
82305a3b1a | ||
|
|
edf021949e | ||
|
|
4ba2cb8737 | ||
|
|
9a17a9676c | ||
|
|
0a54cb0f11 | ||
|
|
24beee1d4c | ||
|
|
95fe9d3dd8 | ||
|
|
a24ae850b6 | ||
|
|
0e303c4cc7 | ||
|
|
d0d2baef24 | ||
|
|
2ac56916dd | ||
|
|
8f5276d10d | ||
|
|
c226ec5b1e | ||
|
|
89dfb252e8 | ||
|
|
940264c537 | ||
|
|
95dcd8984e | ||
|
|
37b5503e77 | ||
|
|
70b6ae1461 | ||
|
|
78f4b13744 | ||
|
|
dff971e9e8 | ||
|
|
c049d3c905 | ||
|
|
1dbdd49a99 | ||
|
|
b6c775eec5 | ||
|
|
164d1573e0 | ||
|
|
80ef4c2d19 | ||
|
|
e114819cc4 | ||
|
|
cdfe5ee18b | ||
|
|
f2feb5edf8 | ||
|
|
22a9b173a5 | ||
|
|
435af883c5 | ||
|
|
154b40b24d | ||
|
|
cd09193924 |
2
.github/workflows/sync-wiki.yml
vendored
2
.github/workflows/sync-wiki.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: fix md links for Gollum
|
||||
run: find ${{ github.workspace }}/docs/ -type f -name "*.md" -exec sed -i -E 's|(\[[^]]+]\()([^)]+\/)*([^).]+).md\)|\1\3)|g' '{}' \;
|
||||
run: find ${{ github.workspace }}/docs/ -type f -name "*.md" -exec sed -i -E 's|(\[[^]]+]\()([^)]+\/)*([^).]+).md(#)*([[:alnum:]]*)\)|\1\3\4\5)|g' '{}' \;
|
||||
- name: fix image links for Gollum
|
||||
run: find ${{ github.workspace }}/docs/ -type f -name "*.png" -exec mv '{}' ${{ github.workspace }}/docs/ \;
|
||||
- uses: Andrew-Chen-Wang/github-wiki-action@v5
|
||||
|
||||
@@ -241,8 +241,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
public Map<Byte, Integer> specifyManaCombo(SpellAbility sa, ColorSet colorSet, int manaAmount, boolean different) {
|
||||
Map<Byte, Integer> result = new HashMap<>();
|
||||
for (int i = 0; i < manaAmount; ++i) {
|
||||
MagicColor.Color chosenColor = chooseColor("", sa, colorSet);
|
||||
Byte chosen = chosenColor == null ? (byte)0 : chosenColor.getColorMask();
|
||||
Byte chosen = chooseColor("", sa, colorSet);
|
||||
if (result.containsKey(chosen)) {
|
||||
result.put(chosen, result.get(chosen) + 1);
|
||||
} else {
|
||||
@@ -1023,22 +1022,19 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagicColor.Color chooseColorAllowColorless(String message, Card card, ColorSet colors) {
|
||||
public byte chooseColorAllowColorless(String message, Card card, ColorSet colors) {
|
||||
final String c = ComputerUtilCard.getMostProminentColor(player.getCardsIn(ZoneType.Hand));
|
||||
byte chosenColorMask = MagicColor.fromName(c);
|
||||
if ((colors.getColor() & chosenColorMask) != 0) {
|
||||
return MagicColor.Color.fromByte(chosenColorMask);
|
||||
return chosenColorMask;
|
||||
}
|
||||
return Iterables.getFirst(colors, MagicColor.Color.COLORLESS);
|
||||
return Iterables.getFirst(colors, MagicColor.Color.COLORLESS).getColorMask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagicColor.Color chooseColor(String message, SpellAbility sa, ColorSet colors) {
|
||||
if (colors.countColors() == 0) {
|
||||
return null;
|
||||
}
|
||||
public byte chooseColor(String message, SpellAbility sa, ColorSet colors) {
|
||||
if (colors.countColors() < 2) {
|
||||
return Iterables.getFirst(colors, MagicColor.Color.WHITE);
|
||||
return Iterables.getFirst(colors, MagicColor.Color.WHITE).getColorMask();
|
||||
}
|
||||
// You may switch on sa.getApi() here and use sa.getParam("AILogic")
|
||||
CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
|
||||
@@ -1049,9 +1045,9 @@ public class PlayerControllerAi extends PlayerController {
|
||||
byte chosenColorMask = MagicColor.fromName(c);
|
||||
|
||||
if ((colors.getColor() & chosenColorMask) != 0) {
|
||||
return MagicColor.Color.fromByte(chosenColorMask);
|
||||
return chosenColorMask;
|
||||
}
|
||||
return Iterables.getFirst(colors, MagicColor.Color.WHITE);
|
||||
return Iterables.getFirst(colors, MagicColor.Color.WHITE).getColorMask();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package forge.card;
|
||||
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.util.Lang;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
//
|
||||
@@ -185,7 +187,25 @@ final class CardFace implements ICardFace, Cloneable {
|
||||
}
|
||||
|
||||
void assignMissingFieldsToVariant(CardFace variant) {
|
||||
if(variant.oracleText == null) variant.oracleText = this.oracleText;
|
||||
if(variant.oracleText == null) {
|
||||
if(variant.flavorName != null && this.oracleText != null) {
|
||||
try {
|
||||
Lang lang = Lang.getInstance();
|
||||
//Rudimentary name replacement. Can't do pronouns, ability words, or flavored keywords. Need to define variant text manually for that.
|
||||
//Regex here checks for the name following either a word boundary or a literal "\n" string, since those haven't yet been converted to line breaks.
|
||||
String flavoredText = this.oracleText.replaceAll("(?<=\\b|\\\\n)" + this.name + "\\b", variant.flavorName);
|
||||
flavoredText = flavoredText.replaceAll("(?<=\\b|\\\\n)" + lang.getNickName(this.name) + "\\b", lang.getNickName(variant.flavorName));
|
||||
variant.oracleText = flavoredText;
|
||||
}
|
||||
catch (PatternSyntaxException ignored) {
|
||||
// Old versions of Android are weird about patterns sometimes. I don't *think* this is such a case but
|
||||
// the documentation is unreliable. May be worth removing this once we're sure it's not a problem.
|
||||
variant.oracleText = this.oracleText;
|
||||
}
|
||||
}
|
||||
else
|
||||
variant.oracleText = this.oracleText;
|
||||
}
|
||||
if(variant.manaCost == null) variant.manaCost = this.manaCost;
|
||||
if(variant.color == null) variant.color = ColorSet.fromManaCost(variant.manaCost);
|
||||
|
||||
|
||||
@@ -504,16 +504,11 @@ public final class CardRules implements ICardCharacteristics {
|
||||
|
||||
CardFace variantMain = ((CardFace) mainPart).getOrCreateFunctionalVariant(variantName);
|
||||
variantMain.setFlavorName(nameParts[0]);
|
||||
//Rudimentary name replacement. Can't do nicknames, pronouns, ability words, or flavored keywords. Need to define variants manually for that.
|
||||
if(mainPart.getOracleText().contains(mainPart.getName()))
|
||||
variantMain.setOracleText(mainPart.getOracleText().replace(mainPart.getName(), nameParts[0]));
|
||||
((CardFace) mainPart).assignMissingFieldsToVariant(variantMain);
|
||||
|
||||
if(otherPart != null) {
|
||||
CardFace variantOther = ((CardFace) otherPart).getOrCreateFunctionalVariant(variantName);
|
||||
variantOther.setFlavorName(nameParts[1]);
|
||||
if(otherPart.getOracleText().contains(otherPart.getName()))
|
||||
variantMain.setOracleText(otherPart.getOracleText().replace(otherPart.getName(), nameParts[1]));
|
||||
((CardFace) otherPart).assignMissingFieldsToVariant(variantOther);
|
||||
}
|
||||
|
||||
|
||||
@@ -1027,6 +1027,22 @@ public class DeckRecognizer {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the magic color by the localised/translated name.
|
||||
* @param localisedName String of localised color name.
|
||||
* @return The string of the magic color.
|
||||
*/
|
||||
public static String getColorNameByLocalisedName(String localisedName) {
|
||||
Localizer localizer = Localizer.getInstance();
|
||||
|
||||
if(localisedName.equals(localizer.getMessage("lblWhite"))) return MagicColor.Constant.WHITE;
|
||||
if(localisedName.equals(localizer.getMessage("lblBlue"))) return MagicColor.Constant.BLUE;
|
||||
if(localisedName.equals(localizer.getMessage("lblBlack"))) return MagicColor.Constant.BLACK;
|
||||
if(localisedName.equals(localizer.getMessage("lblRed"))) return MagicColor.Constant.RED;
|
||||
if(localisedName.equals(localizer.getMessage("lblGreen"))) return MagicColor.Constant.GREEN;
|
||||
|
||||
return "";
|
||||
}
|
||||
public static boolean isDeckName(final String lineAsIs) {
|
||||
if (lineAsIs == null)
|
||||
return false;
|
||||
|
||||
@@ -78,7 +78,7 @@ public class GameAction {
|
||||
private boolean holdCheckingStaticAbilities = false;
|
||||
|
||||
private final static Comparator<StaticAbility> effectOrder = Comparator.comparing(StaticAbility::isCharacteristicDefining).reversed()
|
||||
.thenComparing(s -> s.getHostCard().getLayerTimestamp());
|
||||
.thenComparing(StaticAbility::getTimestamp);
|
||||
|
||||
public GameAction(Game game0) {
|
||||
game = game0;
|
||||
@@ -1103,11 +1103,16 @@ public class GameAction {
|
||||
if (stAb.checkMode(StaticAbilityMode.Continuous) && stAb.zonesCheck()) {
|
||||
staticAbilities.add(stAb);
|
||||
}
|
||||
}
|
||||
if (!co.getStaticCommandList().isEmpty()) {
|
||||
staticList.add(co);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!co.getStaticCommandList().isEmpty()) {
|
||||
staticList.add(co);
|
||||
}
|
||||
for (StaticAbility stAb : co.getHiddenStaticAbilities()) {
|
||||
if (stAb.checkMode(StaticAbilityMode.Continuous) && stAb.zonesCheck()) {
|
||||
staticAbilities.add(stAb);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, true);
|
||||
|
||||
@@ -1333,7 +1338,7 @@ public class GameAction {
|
||||
|
||||
// now the earliest one left is the correct choice
|
||||
List<StaticAbility> statics = Lists.newArrayList(dependencyGraph.vertexSet());
|
||||
statics.sort(Comparator.comparing(s -> s.getHostCard().getLayerTimestamp()));
|
||||
statics.sort(Comparator.comparing(StaticAbility::getTimestamp));
|
||||
|
||||
return statics.get(0);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Lang;
|
||||
@@ -47,7 +48,7 @@ public class AirbendEffect extends SpellAbilityEffect {
|
||||
|
||||
final CardZoneTable triggerList = CardZoneTable.getSimultaneousInstance(sa);
|
||||
|
||||
for (Card c : getTargetCards(sa)) {
|
||||
for (Card c : getCardsfromTargets(sa)) {
|
||||
final Card gameCard = game.getCardState(c, null);
|
||||
// gameCard is LKI in that case, the card is not in game anymore
|
||||
// or the timestamp did change
|
||||
@@ -55,21 +56,27 @@ public class AirbendEffect extends SpellAbilityEffect {
|
||||
if (gameCard == null || !c.equalsWithGameTimestamp(gameCard) || gameCard.isPhasedOut()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!gameCard.canExiledBy(sa, true)) {
|
||||
continue;
|
||||
}
|
||||
handleExiledWith(gameCard, sa);
|
||||
|
||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||
AbilityKey.addCardZoneTableParams(moveParams, triggerList);
|
||||
|
||||
Card movedCard = game.getAction().exile(gameCard, sa, moveParams);
|
||||
SpellAbilityStackInstance si = null;
|
||||
if (gameCard.isInZone(ZoneType.Stack)) {
|
||||
SpellAbility stackSA = game.getStack().getSpellMatchingHost(gameCard);
|
||||
si = game.getStack().getInstanceMatchingSpellAbilityID(stackSA);
|
||||
}
|
||||
|
||||
Card movedCard = game.getAction().exile(gameCard, sa, moveParams);
|
||||
if (movedCard == null || !movedCard.isInZone(ZoneType.Exile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (si != null) {
|
||||
// GameAction.changeZone should really take care of cleaning up SASI when a card from the stack is removed.
|
||||
game.getStack().remove(si);
|
||||
}
|
||||
|
||||
// Effect to cast for 2 from exile
|
||||
Card eff = createEffect(sa, movedCard.getOwner(), "Airbend" + movedCard, hostCard.getImageKey());
|
||||
eff.addRemembered(movedCard);
|
||||
@@ -84,6 +91,7 @@ public class AirbendEffect extends SpellAbilityEffect {
|
||||
|
||||
game.getAction().moveToCommand(eff, sa);
|
||||
}
|
||||
|
||||
triggerList.triggerChangesZoneAll(game, sa);
|
||||
handleExiledWith(triggerList.allCards(), sa);
|
||||
|
||||
|
||||
@@ -30,28 +30,27 @@ public class ChangeTextEffect extends SpellAbilityEffect {
|
||||
|
||||
final String changedColorWordOriginal, changedColorWordNew;
|
||||
if (sa.hasParam("ChangeColorWord")) {
|
||||
// all instances are Choose Choose
|
||||
MagicColor.Color originalColor = null;
|
||||
byte originalColor = 0;
|
||||
final String[] changedColorWordsArray = sa.getParam("ChangeColorWord").split(" ");
|
||||
if (changedColorWordsArray[0].equals("Choose")) {
|
||||
originalColor = sa.getActivatingPlayer().getController().chooseColor(
|
||||
Localizer.getInstance().getMessage("lblChooseColorReplace"), sa, ColorSet.WUBRG);
|
||||
changedColorWordOriginal = TextUtil.capitalize(originalColor.getName());
|
||||
changedColorWordOriginal = TextUtil.capitalize(MagicColor.toLongString(originalColor));
|
||||
} else {
|
||||
changedColorWordOriginal = changedColorWordsArray[0];
|
||||
originalColor = MagicColor.Color.fromByte(MagicColor.fromName(changedColorWordOriginal));
|
||||
originalColor = MagicColor.fromName(changedColorWordOriginal);
|
||||
}
|
||||
|
||||
if (changedColorWordsArray[1].equals("Choose")) {
|
||||
final ColorSet possibleNewColors;
|
||||
if (originalColor == null) { // no original color (ie. any or absent)
|
||||
if (originalColor == 0) { // no original color (ie. any or absent)
|
||||
possibleNewColors = ColorSet.WUBRG;
|
||||
} else { // may choose any except original color
|
||||
possibleNewColors = ColorSet.fromEnums(originalColor).inverse();
|
||||
possibleNewColors = ColorSet.fromMask(originalColor).inverse();
|
||||
}
|
||||
MagicColor.Color newColor = sa.getActivatingPlayer().getController().chooseColor(
|
||||
final byte newColor = sa.getActivatingPlayer().getController().chooseColor(
|
||||
Localizer.getInstance().getMessage("lblChooseNewColor"), sa, possibleNewColors);
|
||||
changedColorWordNew = TextUtil.capitalize(newColor.getName());
|
||||
changedColorWordNew = TextUtil.capitalize(MagicColor.toLongString(newColor));
|
||||
} else {
|
||||
changedColorWordNew = changedColorWordsArray[1];
|
||||
}
|
||||
|
||||
@@ -114,15 +114,15 @@ public class ManaEffect extends SpellAbilityEffect {
|
||||
// just use the first possible color.
|
||||
choice = colorsProduced[differentChoice ? nMana : 0];
|
||||
} else {
|
||||
MagicColor.Color chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa,
|
||||
byte chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa,
|
||||
differentChoice && (colorsNeeded == null || colorsNeeded.length <= nMana) ? fullOptions : colorOptions);
|
||||
if (chosenColor == null)
|
||||
if (chosenColor == 0)
|
||||
throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + p + " color mana choice is empty for " + card.getName());
|
||||
|
||||
if (differentChoice) {
|
||||
fullOptions = ColorSet.fromMask(fullOptions.getColor() - chosenColor.getColorMask());
|
||||
fullOptions = ColorSet.fromMask(fullOptions.getColor() - chosenColor);
|
||||
}
|
||||
choice = chosenColor.getShortName();
|
||||
choice = MagicColor.toShortString(chosenColor);
|
||||
}
|
||||
|
||||
if (nMana > 0) {
|
||||
@@ -157,13 +157,13 @@ public class ManaEffect extends SpellAbilityEffect {
|
||||
mask |= MagicColor.fromName(colorsNeeded.charAt(nChar));
|
||||
}
|
||||
colorMenu = mask == 0 ? ColorSet.WUBRG : ColorSet.fromMask(mask);
|
||||
MagicColor.Color val = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, colorMenu);
|
||||
if (val == null) {
|
||||
byte val = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, colorMenu);
|
||||
if (0 == val) {
|
||||
throw new RuntimeException("ManaEffect::resolve() /*any mana*/ - " + p + " color mana choice is empty for " + card.getName());
|
||||
}
|
||||
|
||||
game.getAction().notifyOfValue(sa, card, val.getSymbol(), p);
|
||||
abMana.setExpressChoice(val.getShortName());
|
||||
game.getAction().notifyOfValue(sa, card, MagicColor.toSymbol(val), p);
|
||||
abMana.setExpressChoice(MagicColor.toShortString(val));
|
||||
}
|
||||
else if (abMana.isSpecialMana()) {
|
||||
String type = abMana.getOrigProduced().split("Special ")[1];
|
||||
@@ -178,22 +178,22 @@ public class ManaEffect extends SpellAbilityEffect {
|
||||
|
||||
for (ManaCostShard s : enchanted.getManaCost()) {
|
||||
ColorSet cs = ColorSet.fromMask(s.getColorMask());
|
||||
MagicColor.Color chosenColor;
|
||||
byte chosenColor;
|
||||
if (cs.isColorless())
|
||||
continue;
|
||||
if (s.isOr2Generic()) { // CR 106.8
|
||||
chosenColor = chooser.getController().chooseColorAllowColorless(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), card, cs);
|
||||
if (chosenColor == MagicColor.Color.COLORLESS) {
|
||||
if (chosenColor == MagicColor.COLORLESS) {
|
||||
generic += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (cs.isMonoColor())
|
||||
chosenColor = MagicColor.Color.fromByte(s.getColorMask());
|
||||
chosenColor = s.getColorMask();
|
||||
else /* (cs.isMulticolor()) */ {
|
||||
chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
|
||||
}
|
||||
sb.append(chosenColor.getShortName());
|
||||
sb.append(MagicColor.toShortString(chosenColor));
|
||||
sb.append(' ');
|
||||
}
|
||||
if (generic > 0) {
|
||||
@@ -239,8 +239,8 @@ public class ManaEffect extends SpellAbilityEffect {
|
||||
if (cs.isMonoColor())
|
||||
sb.append(MagicColor.toShortString(s.getColorMask()));
|
||||
else /* (cs.isMulticolor()) */ {
|
||||
MagicColor.Color chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
|
||||
sb.append(chosenColor.getShortName());
|
||||
byte chosenColor = chooser.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
|
||||
sb.append(MagicColor.toShortString(chosenColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class ManaReflectedEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
if (mask == 0 && !expressChoiceColors.isEmpty() && colors.contains("colorless")) {
|
||||
baseMana = player.getController().chooseColorAllowColorless(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa.getHostCard(), ColorSet.fromMask(mask)).getShortName();
|
||||
baseMana = MagicColor.toShortString(player.getController().chooseColorAllowColorless(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa.getHostCard(), ColorSet.fromMask(mask)));
|
||||
} else {
|
||||
// Nothing set previously so ask player if needed
|
||||
if (mask == 0) {
|
||||
@@ -104,17 +104,17 @@ public class ManaReflectedEffect extends SpellAbilityEffect {
|
||||
} else if (colors.size() == 1) {
|
||||
baseMana = MagicColor.toShortString(colors.iterator().next());
|
||||
} else if (colors.contains("colorless")) {
|
||||
baseMana = player.getController().chooseColorAllowColorless(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa.getHostCard(), ColorSet.fromNames(colors)).getShortName();
|
||||
baseMana = MagicColor.toShortString(player.getController().chooseColorAllowColorless(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa.getHostCard(), ColorSet.fromNames(colors)));
|
||||
} else {
|
||||
baseMana = player.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, ColorSet.fromNames(colors)).getShortName();
|
||||
baseMana = MagicColor.toShortString(player.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, ColorSet.fromNames(colors)));
|
||||
}
|
||||
} else {
|
||||
colorMenu = ColorSet.fromMask(mask);
|
||||
MagicColor.Color color = sa.getActivatingPlayer().getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, colorMenu);
|
||||
if (color == null) {
|
||||
byte color = sa.getActivatingPlayer().getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, colorMenu);
|
||||
if (color == 0) {
|
||||
System.err.println("Unexpected behavior in ManaReflectedEffect: " + sa.getActivatingPlayer() + " - color mana choice is empty for " + sa.getHostCard().getName());
|
||||
}
|
||||
baseMana = color.getShortName();
|
||||
baseMana = MagicColor.toShortString(color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,15 +34,15 @@ public class ReplaceManaEffect extends SpellAbilityEffect {
|
||||
// replace type and amount
|
||||
replaced = sa.getParam("ReplaceMana");
|
||||
if ("Any".equals(replaced)) {
|
||||
MagicColor.Color rs = player.getController().chooseColor("Choose a color", sa, ColorSet.WUBRG);
|
||||
replaced = rs.getShortName();
|
||||
byte rs = player.getController().chooseColor("Choose a color", sa, ColorSet.WUBRG);
|
||||
replaced = MagicColor.toShortString(rs);
|
||||
}
|
||||
} else if (sa.hasParam("ReplaceType")) {
|
||||
// replace color and colorless
|
||||
String color = sa.getParam("ReplaceType");
|
||||
if ("Any".equals(color)) {
|
||||
MagicColor.Color rs = player.getController().chooseColor("Choose a color", sa, ColorSet.WUBRG);
|
||||
color = rs.getShortName();
|
||||
byte rs = player.getController().chooseColor("Choose a color", sa, ColorSet.WUBRG);
|
||||
color = MagicColor.toShortString(rs);
|
||||
} else {
|
||||
// convert in case Color Word used
|
||||
color = MagicColor.toShortString(color);
|
||||
|
||||
@@ -209,7 +209,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
private boolean renowned;
|
||||
private boolean solved;
|
||||
private boolean tributed;
|
||||
private Card suspectedEffect = null;
|
||||
private StaticAbility suspectedStatic = null;
|
||||
|
||||
private SpellAbility manifestedSA;
|
||||
private SpellAbility cloakedSA;
|
||||
@@ -6758,15 +6758,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
return true;
|
||||
}
|
||||
|
||||
public Card getSuspectedEffect() {
|
||||
return this.suspectedEffect;
|
||||
public StaticAbility getSuspectedStatic() {
|
||||
return this.suspectedStatic;
|
||||
}
|
||||
public void setSuspectedEffect(Card effect) {
|
||||
this.suspectedEffect = effect;
|
||||
public void setSuspectedStatic(StaticAbility stAb) {
|
||||
this.suspectedStatic = stAb;
|
||||
}
|
||||
|
||||
public final boolean isSuspected() {
|
||||
return suspectedEffect != null;
|
||||
return suspectedStatic != null;
|
||||
}
|
||||
|
||||
public final boolean setSuspected(final boolean suspected) {
|
||||
@@ -6779,23 +6779,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
return true;
|
||||
}
|
||||
|
||||
suspectedEffect = SpellAbilityEffect.createEffect(null, this, this.getController(), "Suspected Effect", getImageKey(), getGame().getNextTimestamp());
|
||||
suspectedEffect.setRenderForUI(false);
|
||||
suspectedEffect.addRemembered(this);
|
||||
String s = "Mode$ Continuous | AffectedDefined$ Self | AddKeyword$ Menace | AddStaticAbility$ SuspectedCantBlockBy";
|
||||
suspectedStatic = StaticAbility.create(s, this, currentState, true);
|
||||
suspectedStatic.putParam("Timestamp", String.valueOf(getGame().getNextTimestamp()));
|
||||
|
||||
String s = "Mode$ Continuous | AffectedDefined$ RememberedCard | EffectZone$ Command | AddKeyword$ Menace | AddStaticAbility$ SuspectedCantBlockBy";
|
||||
StaticAbility suspectedStatic = suspectedEffect.addStaticAbility(s);
|
||||
String effect = "Mode$ CantBlock | ValidCard$ Creature.Self | Description$ CARDNAME can't block.";
|
||||
suspectedStatic.setSVar("SuspectedCantBlockBy", effect);
|
||||
|
||||
GameCommand until = SpellAbilityEffect.exileEffectCommand(getGame(), suspectedEffect);
|
||||
addLeavesPlayCommand(until);
|
||||
getGame().getAction().moveToCommand(suspectedEffect, null);
|
||||
} else {
|
||||
if (isSuspected()) {
|
||||
getGame().getAction().exileEffect(suspectedEffect);
|
||||
suspectedEffect = null;
|
||||
}
|
||||
suspectedStatic = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -7293,6 +7285,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
||||
}
|
||||
}
|
||||
|
||||
public final FCollectionView<StaticAbility> getHiddenStaticAbilities() {
|
||||
FCollection<StaticAbility> result = new FCollection<>();
|
||||
// Suspected
|
||||
if (this.isInPlay() && this.isSuspected()) {
|
||||
result.add(suspectedStatic);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public final FCollectionView<Trigger> getTriggers() {
|
||||
return currentState.getTriggers();
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ public class CardCopyService {
|
||||
newCopy.setSaddled(copyFrom.isSaddled());
|
||||
if (newCopy.isSaddled()) newCopy.setSaddledByThisTurn(copyFrom.getSaddledByThisTurn());
|
||||
if (copyFrom.isSuspected()) {
|
||||
newCopy.setSuspectedEffect(getLKICopy(copyFrom.getSuspectedEffect(), cachedMap));
|
||||
newCopy.setSuspectedStatic(copyFrom.getSuspectedStatic().copy(newCopy, true));
|
||||
}
|
||||
|
||||
newCopy.setDamageHistory(copyFrom.getDamageHistory());
|
||||
|
||||
@@ -3100,10 +3100,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
Player p = cmd.getController();
|
||||
String prompt = Localizer.getInstance().getMessage("lblChooseAColorFor", cmd.getName());
|
||||
SpellAbility cmdColorsa = new SpellAbility.EmptySa(ApiType.ChooseColor, cmd, p);
|
||||
MagicColor.Color chosenColor = p.getController().chooseColor(prompt, cmdColorsa, ColorSet.WUBRG);
|
||||
cmd.setChosenColors(List.of(chosenColor.getName()));
|
||||
byte chosenColor = p.getController().chooseColor(prompt, cmdColorsa, ColorSet.WUBRG);
|
||||
cmd.setChosenColors(List.of(MagicColor.toLongString(chosenColor)));
|
||||
p.getGame().getAction().notifyOfValue(cmdColorsa, cmd,
|
||||
Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), chosenColor.getName()), p);
|
||||
Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), MagicColor.toLongString(chosenColor)), p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.google.common.collect.Multimap;
|
||||
import forge.LobbyPlayer;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.ICardFace;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.deck.Deck;
|
||||
@@ -266,8 +265,8 @@ public abstract class PlayerController {
|
||||
|
||||
public abstract boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call);
|
||||
|
||||
public abstract MagicColor.Color chooseColor(String message, SpellAbility sa, ColorSet colors);
|
||||
public abstract MagicColor.Color chooseColorAllowColorless(String message, Card c, ColorSet colors);
|
||||
public abstract byte chooseColor(String message, SpellAbility sa, ColorSet colors);
|
||||
public abstract byte chooseColorAllowColorless(String message, Card c, ColorSet colors);
|
||||
public abstract List<String> chooseColors(String message, SpellAbility sa, int min, int max, List<String> options);
|
||||
|
||||
public abstract ICardFace chooseSingleCardFace(SpellAbility sa, String message, Predicate<ICardFace> cpp, String name);
|
||||
|
||||
@@ -297,7 +297,9 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
* conditions are fulfilled.
|
||||
*/
|
||||
private boolean shouldApplyContinuousAbility(final StaticAbilityLayer layer, final boolean previousRun) {
|
||||
return layers.contains(layer) && checkConditions(StaticAbilityMode.Continuous) && (previousRun || getHostCard().getStaticAbilities().contains(this));
|
||||
return layers.contains(layer) && checkConditions(StaticAbilityMode.Continuous) && ( previousRun ||
|
||||
getHostCard().getStaticAbilities().contains(this) ||
|
||||
getHostCard().getHiddenStaticAbilities().contains(this));
|
||||
}
|
||||
|
||||
public final Cost getAttackCost(final Card attacker, final GameEntity target, final List<Card> attackersWithOptionalCost) {
|
||||
@@ -583,6 +585,13 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
.result();
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
if (hasParam("Timestamp")) {
|
||||
return Long.valueOf(getParam("Timestamp"));
|
||||
}
|
||||
return getHostCard().getLayerTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHostCard(Card host) {
|
||||
super.setHostCard(host);
|
||||
|
||||
@@ -25,7 +25,6 @@ import forge.GameCommand;
|
||||
import forge.card.*;
|
||||
import forge.game.Game;
|
||||
import forge.game.StaticEffect;
|
||||
import forge.game.StaticEffects;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.*;
|
||||
@@ -93,12 +92,11 @@ public final class StaticAbilityContinuous {
|
||||
final List<Player> affectedPlayers = StaticAbilityContinuous.getAffectedPlayers(stAb);
|
||||
final Game game = hostCard.getGame();
|
||||
|
||||
final StaticEffects effects = game.getStaticEffects();
|
||||
final StaticEffect se = effects.getStaticEffect(stAb);
|
||||
final StaticEffect se = game.getStaticEffects().getStaticEffect(stAb);
|
||||
se.setAffectedCards(affectedCards);
|
||||
se.setAffectedPlayers(affectedPlayers);
|
||||
se.setParams(params);
|
||||
se.setTimestamp(hostCard.getLayerTimestamp());
|
||||
se.setTimestamp(stAb.getTimestamp());
|
||||
|
||||
// nothing more to do
|
||||
if (stAb.hasParam("Affected") && affectedPlayers.isEmpty() && affectedCards.isEmpty()) {
|
||||
|
||||
@@ -470,16 +470,13 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagicColor.Color chooseColor(String message, SpellAbility sa, ColorSet colors) {
|
||||
if (colors.countColors() == 0) {
|
||||
return null;
|
||||
}
|
||||
return Iterables.getFirst(colors, MagicColor.Color.WHITE);
|
||||
public byte chooseColor(String message, SpellAbility sa, ColorSet colors) {
|
||||
return Iterables.getFirst(colors, MagicColor.Color.WHITE).getColorMask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagicColor.Color chooseColorAllowColorless(String message, Card card, ColorSet colors) {
|
||||
return Iterables.getFirst(colors, MagicColor.Color.COLORLESS);
|
||||
public byte chooseColorAllowColorless(String message, Card card, ColorSet colors) {
|
||||
return Iterables.getFirst(colors, MagicColor.Color.COLORLESS).getColorMask();
|
||||
}
|
||||
|
||||
private CardCollection chooseItems(CardCollectionView items, int amount) {
|
||||
|
||||
@@ -1556,7 +1556,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Amulet of the Deceiver",
|
||||
"equipmentSlot": "Head",
|
||||
"equipmentSlot": "Neck",
|
||||
"iconName": "MoxJet",
|
||||
"effect": {
|
||||
"lifeModifier": -1,
|
||||
|
||||
@@ -1343,7 +1343,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Helm of Obedience",
|
||||
"equipmentSlot": "Head",
|
||||
"equipmentSlot": "Neck",
|
||||
"iconName": "MoxJet",
|
||||
"effect": {
|
||||
"lifeModifier": -1,
|
||||
|
||||
@@ -1556,7 +1556,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Amulet of the Deceiver",
|
||||
"equipmentSlot": "Head",
|
||||
"equipmentSlot": "Neck",
|
||||
"iconName": "MoxJet",
|
||||
"effect": {
|
||||
"lifeModifier": -1,
|
||||
|
||||
@@ -145,3 +145,4 @@ Final Fantasy, 3/6/FIN, FIN
|
||||
Alchemy: Innistrad, 3/6/ISD, YMID
|
||||
Edge of Eternities, 3/6/EOE, EOE
|
||||
Marvel's Spider-Man, 3/6/SPM, SPM
|
||||
Avatar: The Last Airbender, 3/6/TLA, TLA
|
||||
@@ -0,0 +1,23 @@
|
||||
Name:Aang, Swift Savior
|
||||
ManaCost:1 W U
|
||||
Types:Legendary Creature Human Avatar Ally
|
||||
PT:2/3
|
||||
K:Flash
|
||||
K:Flying
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigAirbend | TriggerDescription$ When NICKNAME enters, airbend up to one other target creature or spell. (Exile it. While it's exiled, its owner may cast it for {2} rather than its mana cost.)
|
||||
SVar:TrigAirbend:DB$ Airbend | ValidTgts$ Creature.Other,Card.inZoneStack | TgtPrompt$ Select up to another target creature or spell | TgtZone$ Battlefield,Stack | TargetMin$ 0
|
||||
A:AB$ SetState | Cost$ Waterbend<8> | Defined$ Self | Mode$ Transform | SpellDescription$ Transform NICKNAME.
|
||||
AlternateMode:DoubleFaced
|
||||
Oracle:Flash\nFlying\nWhen Aang enters, airbend up to one other target creature or spell. (Exile it. While it's exiled, its owner may cast it for {2} rather than its mana cost.)\nWaterbend {8}: Transform Aang.
|
||||
|
||||
ALTERNATE
|
||||
|
||||
Name:Aang and La, Ocean's Fury
|
||||
ManaCost:no cost
|
||||
Types:Legendary Creature Avatar Spirit Ally
|
||||
PT:5/5
|
||||
K:Reach
|
||||
K:Trample
|
||||
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPutCounterAll | TriggerDescription$ Whenever NICKNAME attack, put a +1/+1 counter on each tapped creature you control.
|
||||
SVar:TrigPutCounterAll:DB$ PutCounterAll | ValidCards$ Creature.YouCtrl+tapped | CounterType$ P1P1 | CounterNum$ 1
|
||||
Oracle:Reach, trample\nWhenever Aang and La attack, put a +1/+1 counter on each tapped creature you control.
|
||||
10
forge-gui/res/cardsfolder/b/benevolent_river_spirit.txt
Normal file
10
forge-gui/res/cardsfolder/b/benevolent_river_spirit.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Name:Benevolent River Spirit
|
||||
ManaCost:U U
|
||||
Types:Creature Spirit
|
||||
PT:4/5
|
||||
S:Mode$ RaiseCost | ValidCard$ Card.Self | Activator$ You | Type$ Spell | Cost$ Waterbend<5> | EffectZone$ All | Description$ As an additional cost to cast this spell, waterbend {5}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)
|
||||
K:Flying
|
||||
K:Ward:2
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigScry | TriggerDescription$ When this creature enters, scry 2.
|
||||
SVar:TrigScry:DB$ Scry | ScryNum$ 2
|
||||
Oracle:As an additional cost to cast this spell, waterbend {5}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)\nFlying, ward {2} (Whenever this creature becomes the target of a spell or ability an opponent controls, counter it unless that player pays {2}.)\nWhen this creature enters, scry 2.
|
||||
8
forge-gui/res/cardsfolder/c/crashing_wave.txt
Normal file
8
forge-gui/res/cardsfolder/c/crashing_wave.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Name:Crashing Wave
|
||||
ManaCost:U U
|
||||
Types:Sorcery
|
||||
S:Mode$ RaiseCost | ValidCard$ Card.Self | Activator$ You | Type$ Spell | Cost$ Waterbend<X> | EffectZone$ All | Description$ As an additional cost to cast this spell, waterbend {X}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)
|
||||
A:SP$ Tap | TargetMin$ 0 | TargetMax$ X | TgtPrompt$ Select up to X target creatures to tap | ValidTgts$ Creature | SubAbility$ DBPutCounter
|
||||
SVar:DBPutCounter:DB$ PutCounter | Choices$ Creature.tapped+OppCtrl | ChoiceTitle$ Choose any number of tapped creatures your opponents control to distribute counters to | CounterType$ STUN | CounterNum$ 3 | ChoiceAmount$ 3 | DividedAsYouChoose$ 3
|
||||
SVar:X:Count$xPaid
|
||||
Oracle:As an additional cost to cast this spell, waterbend {X}. (While paying a waterbend cost, you can tap your artifacts and creatures to help. Each one pays for {1}.)\nTap up to X target creatures, then distribute three stun counters among tapped creatures your opponents control. (If a permanent with a stun counter would become untapped, remove one from it instead.)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user