mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +00:00
Merge branch 'master' into Attractions
# Conflicts: # forge-core/src/main/java/forge/deck/DeckFormat.java
This commit is contained in:
@@ -50,7 +50,6 @@ import forge.util.Aggregates;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.Visitor;
|
||||
import forge.util.collect.FCollection;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
@@ -327,7 +326,9 @@ public class Game {
|
||||
int plId = 0;
|
||||
for (RegisteredPlayer psc : players0) {
|
||||
IGameEntitiesFactory factory = (IGameEntitiesFactory)psc.getPlayer();
|
||||
Player pl = factory.createIngamePlayer(this, plId++);
|
||||
// If the Registered Player already has a pre-assigned ID, use that. Otherwise, assign a new one.
|
||||
Integer id = psc.getId();
|
||||
Player pl = factory.createIngamePlayer(this, id == null ? plId++ : id);
|
||||
allPlayers.add(pl);
|
||||
ingamePlayers.add(pl);
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ public final class GameActionUtil {
|
||||
// do only non intrinsic
|
||||
if (iSa.isSpell() && !iSa.isIntrinsic()) {
|
||||
alternatives.add(iSa);
|
||||
alternatives.addAll(getMayPlaySpellOptions(iSa, source, activator, altCostOnly));
|
||||
alternatives.addAll(getMayPlaySpellOptions(iSa, stackCopy, activator, altCostOnly));
|
||||
// currently only AltCost get added this way
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +84,11 @@ public abstract class SpellAbilityEffect {
|
||||
String spellDesc = CardTranslation.translateSingleDescriptionText(rawSDesc,
|
||||
sa.getHostCard().getName());
|
||||
|
||||
int idx = spellDesc.indexOf("(");
|
||||
if (idx > 0) { //trim reminder text from StackDesc
|
||||
spellDesc = spellDesc.substring(0, spellDesc.indexOf("(") - 1);
|
||||
//trim reminder text from StackDesc
|
||||
int idxL = spellDesc.indexOf(" (");
|
||||
int idxR = spellDesc.indexOf(")");
|
||||
if (idxL > 0 && idxR > idxL) {
|
||||
spellDesc = spellDesc.replace(spellDesc.substring(idxL, idxR + 1), "");
|
||||
}
|
||||
|
||||
if (reps != null) {
|
||||
|
||||
@@ -26,9 +26,10 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
|
||||
@Override
|
||||
protected String getStackDescription(SpellAbility sa) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(Lang.joinHomogenous(getDefinedPlayersOrTargeted(sa)));
|
||||
sb.append("chooses from a list.");
|
||||
final List<Player> players = getDefinedPlayersOrTargeted(sa);
|
||||
|
||||
sb.append(Lang.joinHomogenous(players));
|
||||
sb.append(players.size() == 1 ? " chooses" : " choose").append(" from a list.");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -120,8 +120,8 @@ public class DebuffEffect extends SpellAbilityEffect {
|
||||
ProtectionFromColor = true;
|
||||
}
|
||||
if (ProtectionFromColor) {
|
||||
// Split "Protection from all colors" into extra Protection from <color>
|
||||
String allColors = "Protection from all colors";
|
||||
// Split "Protection from each color" into extra Protection from <color>
|
||||
String allColors = "Protection from each color";
|
||||
if (tgtC.hasKeyword(allColors)) {
|
||||
final List<String> allColorsProtect = Lists.newArrayList();
|
||||
|
||||
@@ -134,7 +134,7 @@ public class DebuffEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
// Extra for Spectra Ward
|
||||
allColors = "Protection:Card.nonColorless:all colors:Aura";
|
||||
allColors = "Protection:Card.nonColorless:each color:Aura";
|
||||
if (tgtC.hasKeyword(allColors)) {
|
||||
final List<String> allColorsProtect = Lists.newArrayList();
|
||||
|
||||
|
||||
@@ -180,12 +180,8 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
for (final Player p : repeatPlayers) {
|
||||
if (optional) {
|
||||
if (!p.getController().confirmAction(repeat, null, sa.getParam("RepeatOptionalMessage"), null)) {
|
||||
continue;
|
||||
} else if (sa.hasParam("RememberDeciders")) {
|
||||
source.addRemembered(p);
|
||||
}
|
||||
if (optional && !p.getController().confirmAction(repeat, null, sa.getParam("RepeatOptionalMessage"), null)) {
|
||||
continue;
|
||||
}
|
||||
if (nextTurn) {
|
||||
game.getCleanup().addUntil(p, new GameCommand() {
|
||||
|
||||
@@ -2258,7 +2258,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
// If no colon exists in Madness keyword, it must have been granted and assumed the cost from host
|
||||
sbLong.append("Madness ").append(this.getManaCost()).append(" (").append(inst.getReminderText());
|
||||
sbLong.append(")").append("\r\n");
|
||||
} else if (keyword.startsWith("Emerge") || keyword.startsWith("Reflect")) {
|
||||
} else if (keyword.startsWith("Reflect")) {
|
||||
final String[] k = keyword.split(":");
|
||||
sbLong.append(k[0]).append(" ").append(ManaCostParser.parse(k[1]));
|
||||
sbLong.append(" (").append(inst.getReminderText()).append(")");
|
||||
@@ -2325,6 +2325,16 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
}
|
||||
sbLong.append("\r\n");
|
||||
} else if (keyword.startsWith("Emerge")) {
|
||||
final String[] k = keyword.split(":");
|
||||
sbLong.append(k[0]);
|
||||
if (k.length > 2) {
|
||||
sbLong.append(" from ").append(k[2].toLowerCase());
|
||||
}
|
||||
sbLong.append(" ").append(ManaCostParser.parse(k[1]));
|
||||
sbLong.append(" (").append(inst.getReminderText()).append(")");
|
||||
sbLong.append("\r\n");
|
||||
|
||||
} else if (inst.getKeyword().equals(Keyword.COMPANION)) {
|
||||
sbLong.append("Companion — ");
|
||||
sbLong.append(((Companion)inst).getDescription());
|
||||
@@ -2484,7 +2494,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|| keyword.equals("Undaunted") || keyword.startsWith("Monstrosity")
|
||||
|| keyword.startsWith("Embalm") || keyword.equals("Prowess")
|
||||
|| keyword.startsWith("Eternalize") || keyword.startsWith("Reinforce")
|
||||
|| keyword.startsWith("Champion") || keyword.startsWith("Prowl") || keyword.startsWith("Adapt")
|
||||
|| keyword.startsWith("Champion") || keyword.startsWith("Freerunning") || keyword.startsWith("Prowl") || keyword.startsWith("Adapt")
|
||||
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Chapter")
|
||||
|| keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap")
|
||||
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")
|
||||
@@ -3002,7 +3012,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
} else if (keyword.startsWith("Starting intensity")) {
|
||||
sbAfter.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n");
|
||||
} else if (keyword.startsWith("Escalate") || keyword.startsWith("Buyback")
|
||||
|| keyword.startsWith("Prowl")) {
|
||||
|| keyword.startsWith("Freerunning") || keyword.startsWith("Prowl")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final String manacost = k[1];
|
||||
final Cost cost = new Cost(manacost, false);
|
||||
@@ -6757,7 +6767,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
pW = true;
|
||||
protectKey += "W";
|
||||
}
|
||||
} else if (kw.contains("all colors")) {
|
||||
} else if (kw.contains("each color")) {
|
||||
protectKey += "allcolors:";
|
||||
} else if (kw.equals("Protection from everything")) {
|
||||
protectKey += "everything:";
|
||||
|
||||
@@ -676,7 +676,7 @@ public class CardFactoryUtil {
|
||||
validSource = "Green" + (damage ? "Source" : "");
|
||||
} else if (protectType.equals("colorless")) {
|
||||
validSource = "Colorless" + (damage ? "Source" : "");
|
||||
} else if (protectType.equals("all colors")) {
|
||||
} else if (protectType.equals("each color")) {
|
||||
validSource = "nonColorless" + (damage ? "Source" : "");
|
||||
} else if (protectType.equals("everything")) {
|
||||
return "";
|
||||
@@ -2830,16 +2830,22 @@ public class CardFactoryUtil {
|
||||
} else if (keyword.startsWith("Emerge")) {
|
||||
final String[] kw = keyword.split(":");
|
||||
String costStr = kw[1];
|
||||
final SpellAbility sa = card.getFirstSpellAbility();
|
||||
String validStr = kw.length > 2 ? kw[2] : "Creature";
|
||||
String desc = "(Emerge";
|
||||
if (kw.length > 2) {
|
||||
desc += " from " + kw[2].toLowerCase();
|
||||
}
|
||||
desc += ")";
|
||||
|
||||
final SpellAbility sa = card.getFirstSpellAbility();
|
||||
final SpellAbility newSA = sa.copyWithDefinedCost(new Cost(costStr, false));
|
||||
|
||||
newSA.getRestrictions().setIsPresent("Creature.YouCtrl+CanBeSacrificedBy");
|
||||
newSA.getRestrictions().setIsPresent(validStr + ".YouCtrl+CanBeSacrificedBy");
|
||||
newSA.putParam("Secondary", "True");
|
||||
newSA.setAlternativeCost(AlternativeCost.Emerge);
|
||||
|
||||
newSA.setDescription(sa.getDescription() + " (Emerge)");
|
||||
newSA.putParam("AfterDescription", "(Emerge)");
|
||||
newSA.setDescription(sa.getDescription() + " " + desc);
|
||||
newSA.putParam("AfterDescription", desc);
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
} else if (keyword.startsWith("Embalm")) {
|
||||
@@ -3105,6 +3111,27 @@ public class CardFactoryUtil {
|
||||
// instantiate attach ability
|
||||
final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card);
|
||||
inst.addSpellAbility(sa);
|
||||
} else if (keyword.startsWith("Freerunning")) {
|
||||
final String[] k = keyword.split(":");
|
||||
final Cost freerunningCost = new Cost(k[1], false);
|
||||
final SpellAbility newSA = card.getFirstSpellAbility().copyWithDefinedCost(freerunningCost);
|
||||
|
||||
if (host.isInstant() || host.isSorcery()) {
|
||||
newSA.putParam("Secondary", "True");
|
||||
}
|
||||
newSA.putParam("PrecostDesc", "Freerunning");
|
||||
newSA.putParam("CostDesc", ManaCostParser.parse(k[1]));
|
||||
|
||||
// makes new SpellDescription
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(newSA.getCostDescription());
|
||||
sb.append("(").append(inst.getReminderText()).append(")");
|
||||
newSA.setDescription(sb.toString());
|
||||
|
||||
newSA.setAlternativeCost(AlternativeCost.Freerunning);
|
||||
|
||||
newSA.setIntrinsic(intrinsic);
|
||||
inst.addSpellAbility(newSA);
|
||||
} else if (keyword.startsWith("Fuse") && card.getStateName().equals(CardStateName.Original)) {
|
||||
final SpellAbility sa = AbilityFactory.buildFusedAbility(card.getCard());
|
||||
card.addSpellAbility(sa);
|
||||
@@ -3900,7 +3927,8 @@ public class CardFactoryUtil {
|
||||
final String[] k = keyword.split(":");
|
||||
|
||||
sbDesc.append(" from ").append(k[2]);
|
||||
sbValid.append("| ValidSource$ ").append(k[1]);
|
||||
final String param = k[2].contains("abilities") ? "ValidSA$ " : "ValidSource$ ";
|
||||
sbValid.append("| ").append(param).append(k[1]);
|
||||
}
|
||||
|
||||
String effect = "Mode$ CantTarget | ValidCard$ Card.Self | Secondary$ True"
|
||||
|
||||
@@ -2120,6 +2120,14 @@ public class CardProperty {
|
||||
}
|
||||
List<String> nameList = Lists.newArrayList(names.split(";"));
|
||||
|
||||
return nameList.contains(card.getName());
|
||||
} else if (property.equals("NotedNameAetherSearcher")) {
|
||||
String names = sourceController.getDraftNotes().get("Aether Searcher");
|
||||
if (names == null || names.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
List<String> nameList = Lists.newArrayList(names.split(";"));
|
||||
|
||||
return nameList.contains(card.getName());
|
||||
} else if (property.equals("NotedTypes")) {
|
||||
// Should Paliano Vanguard be hardcoded here or part of the property?
|
||||
|
||||
@@ -57,7 +57,7 @@ public final class CardUtil {
|
||||
"Cycling", "Echo", "Kicker", "Flashback", "Madness", "Morph",
|
||||
"Affinity", "Entwine", "Splice", "Ninjutsu",
|
||||
"Transmute", "Replicate", "Recover", "Squad", "Suspend", "Aura swap",
|
||||
"Fortify", "Transfigure", "Champion", "Evoke", "Prowl",
|
||||
"Fortify", "Transfigure", "Champion", "Evoke", "Prowl", "Freerunning",
|
||||
"Reinforce", "Unearth", "Level up", "Miracle", "Overload", "Cleave",
|
||||
"Scavenge", "Encore", "Bestow", "Outlast", "Dash", "Surge", "Emerge", "Hexproof:",
|
||||
"etbCounter", "Reflect", "Ward").build();
|
||||
|
||||
@@ -326,8 +326,28 @@ public class CardView extends GameEntityView {
|
||||
void updateDamage(Card c) {
|
||||
set(TrackableProperty.Damage, c.getDamage());
|
||||
updateLethalDamage(c);
|
||||
//update CrackOverlay (currently 16 overlays)
|
||||
set(TrackableProperty.CrackOverlay, c.getDamage() > 0 ? MyRandom.getRandom().nextInt(16) : 0);
|
||||
//get crackoverlay by level of damage light 0, medium 1, heavy 2, max 3
|
||||
int randCrackLevel = 0;
|
||||
if (c.getDamage() > 0) {
|
||||
switch (c.getDamage()) {
|
||||
case 1:
|
||||
case 2:
|
||||
randCrackLevel = 0;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
randCrackLevel = 1;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
randCrackLevel = 2;
|
||||
break;
|
||||
default:
|
||||
randCrackLevel = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
set(TrackableProperty.CrackOverlay, randCrackLevel);
|
||||
}
|
||||
|
||||
public int getAssignedDamage() {
|
||||
@@ -1466,6 +1486,7 @@ public class CardView extends GameEntityView {
|
||||
public String getKeywordKey() { return get(TrackableProperty.KeywordKey); }
|
||||
public String getProtectionKey() { return get(TrackableProperty.ProtectionKey); }
|
||||
public String getHexproofKey() { return get(TrackableProperty.HexproofKey); }
|
||||
public boolean hasAnnihilator() { return get(TrackableProperty.HasAnnihilator); }
|
||||
public boolean hasDeathtouch() { return get(TrackableProperty.HasDeathtouch); }
|
||||
public boolean hasToxic() { return get(TrackableProperty.HasToxic); }
|
||||
public boolean hasDevoid() { return get(TrackableProperty.HasDevoid); }
|
||||
@@ -1473,6 +1494,7 @@ public class CardView extends GameEntityView {
|
||||
public boolean hasDivideDamage() { return get(TrackableProperty.HasDivideDamage); }
|
||||
public boolean hasDoubleStrike() { return get(TrackableProperty.HasDoubleStrike); }
|
||||
public boolean hasDoubleTeam() { return get(TrackableProperty.HasDoubleTeam); }
|
||||
public boolean hasExalted() { return get(TrackableProperty.HasExalted); }
|
||||
public boolean hasFirstStrike() { return get(TrackableProperty.HasFirstStrike); }
|
||||
public boolean hasFlying() { return get(TrackableProperty.HasFlying); }
|
||||
public boolean hasFear() { return get(TrackableProperty.HasFear); }
|
||||
@@ -1542,6 +1564,7 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
void updateKeywords(Card c, CardState state) {
|
||||
c.updateKeywordsCache(state);
|
||||
set(TrackableProperty.HasAnnihilator, c.hasKeyword(Keyword.ANNIHILATOR, state));
|
||||
set(TrackableProperty.HasDeathtouch, c.hasKeyword(Keyword.DEATHTOUCH, state));
|
||||
set(TrackableProperty.HasToxic, c.hasKeyword(Keyword.TOXIC, state));
|
||||
set(TrackableProperty.HasDevoid, c.hasKeyword(Keyword.DEVOID, state));
|
||||
@@ -1549,6 +1572,7 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.HasDivideDamage, c.hasKeyword("You may assign CARDNAME's combat damage divided as " +
|
||||
"you choose among defending player and/or any number of creatures they control."));
|
||||
set(TrackableProperty.HasDoubleStrike, c.hasKeyword(Keyword.DOUBLE_STRIKE, state));
|
||||
set(TrackableProperty.HasExalted, c.hasKeyword(Keyword.EXALTED, state));
|
||||
set(TrackableProperty.HasFirstStrike, c.hasKeyword(Keyword.FIRST_STRIKE, state));
|
||||
set(TrackableProperty.HasFlying, c.hasKeyword(Keyword.FLYING, state));
|
||||
set(TrackableProperty.HasFear, c.hasKeyword(Keyword.FEAR, state));
|
||||
|
||||
@@ -223,7 +223,15 @@ public class CostAdjustment {
|
||||
// Reduce cost
|
||||
int sumGeneric = 0;
|
||||
if (sa.hasParam("ReduceCost")) {
|
||||
sumGeneric += AbilityUtils.calculateAmount(originalCard, sa.getParam("ReduceCost"), sa);
|
||||
String cst = sa.getParam("ReduceCost");
|
||||
String amt = sa.getParamOrDefault("ReduceAmount", cst);
|
||||
int num = AbilityUtils.calculateAmount(originalCard, amt, sa);
|
||||
|
||||
if (sa.hasParam("ReduceAmount") && num > 0) {
|
||||
cost.subtractManaCost(new ManaCost(new ManaCostParser(Strings.repeat(cst + " ", num))));
|
||||
} else {
|
||||
sumGeneric += num;
|
||||
}
|
||||
}
|
||||
|
||||
while (!reduceAbilities.isEmpty()) {
|
||||
@@ -379,9 +387,15 @@ public class CostAdjustment {
|
||||
}
|
||||
|
||||
private static void adjustCostByEmerge(final ManaCostBeingPaid cost, final SpellAbility sa) {
|
||||
CardCollectionView canEmerge = CardLists.filter(sa.getActivatingPlayer().getCreaturesInPlay(), CardPredicates.canBeSacrificedBy(sa, false));
|
||||
String kw = sa.getKeyword().getOriginal();
|
||||
String k[] = kw.split(":");
|
||||
String validStr = k.length > 2 ? k[2] : "Creature";
|
||||
Player p = sa.getActivatingPlayer();
|
||||
CardCollectionView canEmerge = CardLists.filter(p.getCardsIn(ZoneType.Battlefield),
|
||||
CardPredicates.restriction(validStr, p, sa.getHostCard(), sa),
|
||||
CardPredicates.canBeSacrificedBy(sa, false));
|
||||
|
||||
final CardCollectionView toSacList = sa.getHostCard().getController().getController().choosePermanentsToSacrifice(sa, 0, 1, canEmerge, "Creature");
|
||||
final CardCollectionView toSacList = p.getController().choosePermanentsToSacrifice(sa, 0, 1, canEmerge, validStr);
|
||||
|
||||
if (toSacList.isEmpty()) {
|
||||
return;
|
||||
|
||||
13
forge-game/src/main/java/forge/game/keyword/Emerge.java
Normal file
13
forge-game/src/main/java/forge/game/keyword/Emerge.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package forge.game.keyword;
|
||||
|
||||
public class Emerge extends KeywordWithCostAndType {
|
||||
protected void parse(String details) {
|
||||
final String[] k = details.split(":");
|
||||
if (k.length < 2) {
|
||||
super.parse("Creature:" + k[0]);
|
||||
} else {
|
||||
// Flip parameters
|
||||
super.parse(k[1] + ":" + k[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public enum Keyword {
|
||||
DREDGE("Dredge", KeywordWithAmount.class, false, "If you would draw a card, instead you may put exactly {%d:card} from the top of your library into your graveyard. If you do, return this card from your graveyard to your hand. Otherwise, draw a card."),
|
||||
ECHO("Echo", KeywordWithCost.class, false, "At the beginning of your upkeep, if this permanent came under your control since the beginning of your last upkeep, sacrifice it unless you pay %s."),
|
||||
EMBALM("Embalm", KeywordWithCost.class, false, "%s, Exile this card from your graveyard: Create a token that's a copy of this card, except it's white, it has no mana cost, and it's a Zombie in addition to its other types. Embalm only as a sorcery."),
|
||||
EMERGE("Emerge", KeywordWithCost.class, false, "You may cast this spell by sacrificing a creature and paying the emerge cost reduced by that creature's mana value."),
|
||||
EMERGE("Emerge", Emerge.class, false, "You may cast this spell by sacrificing {1:%2$s} and paying the emerge cost reduced by that %2$s's mana value."),
|
||||
ENCHANT("Enchant", KeywordWithType.class, false, "Target a %s as you cast this. This card enters the battlefield attached to that %s."),
|
||||
ENCORE("Encore", KeywordWithCost.class, false, "%s, Exile this card from your graveyard: For each opponent, create a token copy that attacks that opponent this turn if able. They gain haste. Sacrifice them at the beginning of the next end step. Activate only as a sorcery."),
|
||||
ENLIST("Enlist", SimpleKeyword.class, false, "As this creature attacks, you may tap a nonattacking creature you control without summoning sickness. When you do, add its power to this creature’s until end of turn."),
|
||||
@@ -95,6 +95,7 @@ public enum Keyword {
|
||||
FOR_MIRRODIN("For Mirrodin", SimpleKeyword.class, false, "When this Equipment enters the battlefield, create a 2/2 red Rebel creature token, then attach this to it."),
|
||||
FORETELL("Foretell", KeywordWithCost.class, false, "During your turn, you may pay {2} and exile this card from your hand face down. Cast it on a later turn for its foretell cost."),
|
||||
FORTIFY("Fortify", KeywordWithCost.class, false, "%s: Attach to target land you control. Fortify only as a sorcery."),
|
||||
FREERUNNING("Freerunning", KeywordWithCost.class, false, "You may cast this spell for its freerunning cost if you dealt combat damage to a player this turn with an Assassin or commander."),
|
||||
FRENZY("Frenzy", KeywordWithAmount.class, false, "Whenever this creature attacks and isn't blocked, it gets +%d/+0 until end of turn."),
|
||||
FRIENDS_FOREVER("Friends forever", Partner.class, true, "You can have two commanders if both have friends forever."),
|
||||
FUSE("Fuse", SimpleKeyword.class, true, "You may cast one or both halves of this card from your hand."),
|
||||
|
||||
@@ -2150,6 +2150,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
return !game.getDamageDoneThisTurn(true, true, sb.toString(), "Player", null, this, null).isEmpty();
|
||||
}
|
||||
|
||||
public final boolean hasFreerunning() {
|
||||
return !game.getDamageDoneThisTurn(true, true, "Card.Assassin+YouCtrl,Card.IsCommander+YouCtrl", "Player", null, this, null).isEmpty();
|
||||
}
|
||||
|
||||
public final void setLibrarySearched(final int l) {
|
||||
numLibrarySearchedOwn = l;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package forge.game.player;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -15,6 +14,7 @@ import forge.util.Expressions;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -264,6 +264,10 @@ public class PlayerProperty {
|
||||
if (source.getChosenPlayer() == null || !source.getChosenPlayer().equals(player)) {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("NotedDefender")) {
|
||||
String tracker = player.getDraftNotes().getOrDefault("Cogwork Tracker", "");
|
||||
|
||||
return Iterables.contains(Arrays.asList(tracker.split(",")), String.valueOf(player));
|
||||
} else if (property.startsWith("life")) {
|
||||
int life = player.getLife();
|
||||
int amount = AbilityUtils.calculateAmount(source, property.substring(6), spellAbility);
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package forge.game.player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.LobbyPlayer;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
@@ -16,6 +10,11 @@ import forge.game.GameType;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class RegisteredPlayer {
|
||||
private final Deck originalDeck; // never return or modify this instance (it's a reference to game resources)
|
||||
private Deck currentDeck;
|
||||
@@ -38,6 +37,7 @@ public class RegisteredPlayer {
|
||||
private List<PaperCard> vanguardAvatars = null;
|
||||
private PaperCard planeswalker = null;
|
||||
private int teamNumber = -1; // members of teams with negative id will play FFA.
|
||||
private Integer id = null;
|
||||
private boolean randomFoil = false;
|
||||
private boolean enableETBCountersEffect = false;
|
||||
|
||||
@@ -46,6 +46,14 @@ public class RegisteredPlayer {
|
||||
restoreDeck();
|
||||
}
|
||||
|
||||
public final Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public final void setId(Integer id0) {
|
||||
id = id0;
|
||||
}
|
||||
|
||||
public final Deck getDeck() {
|
||||
return currentDeck;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ public enum AlternativeCost {
|
||||
Evoke,
|
||||
Flashback,
|
||||
Foretold,
|
||||
Freerunning,
|
||||
Madness,
|
||||
MTMtE, // More Than Meets the Eye (Transformers Universes Beyond)
|
||||
Mutate,
|
||||
|
||||
@@ -1549,6 +1549,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
return isAlternativeCost(AlternativeCost.Evoke);
|
||||
}
|
||||
|
||||
public final boolean isFreerunning() {
|
||||
return isAlternativeCost(AlternativeCost.Freerunning);
|
||||
}
|
||||
|
||||
public final boolean isMadness() {
|
||||
return isAlternativeCost(AlternativeCost.Madness);
|
||||
}
|
||||
|
||||
@@ -444,6 +444,11 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (sa.isFreerunning()) {
|
||||
if (!activator.hasFreerunning()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.getIsPresent() != null) {
|
||||
FCollection<GameObject> list;
|
||||
if (getPresentDefined() != null) {
|
||||
|
||||
@@ -70,7 +70,8 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
||||
}
|
||||
|
||||
/** {@inheritDoc}
|
||||
* @param runParams*/
|
||||
* @param runParams
|
||||
**/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
final SpellAbility spellAbility = (SpellAbility) runParams.get(AbilityKey.SpellAbility);
|
||||
|
||||
@@ -144,6 +144,7 @@ public enum TrackableProperty {
|
||||
CountBasicLandTypes(TrackableTypes.IntegerType),
|
||||
|
||||
KeywordKey(TrackableTypes.StringType),
|
||||
HasAnnihilator(TrackableTypes.BooleanType),
|
||||
HasDeathtouch(TrackableTypes.BooleanType),
|
||||
HasToxic(TrackableTypes.BooleanType),
|
||||
HasDevoid(TrackableTypes.BooleanType),
|
||||
@@ -151,6 +152,7 @@ public enum TrackableProperty {
|
||||
HasDivideDamage(TrackableTypes.BooleanType),
|
||||
HasDoubleStrike(TrackableTypes.BooleanType),
|
||||
HasDoubleTeam(TrackableTypes.BooleanType),
|
||||
HasExalted(TrackableTypes.BooleanType),
|
||||
HasFirstStrike(TrackableTypes.BooleanType),
|
||||
HasFlying(TrackableTypes.BooleanType),
|
||||
HasFear(TrackableTypes.BooleanType),
|
||||
|
||||
Reference in New Issue
Block a user