mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Merge branch 'master' into AFC_Cards_20210717
This commit is contained in:
@@ -153,8 +153,7 @@ public final class GameActionUtil {
|
||||
final StringBuilder sb = new StringBuilder(sa.getDescription());
|
||||
if (!source.equals(host)) {
|
||||
sb.append(" by ");
|
||||
if ((host.isEmblem() || host.getType().hasSubtype("Effect"))
|
||||
&& host.getEffectSource() != null) {
|
||||
if ((host.isImmutable()) && host.getEffectSource() != null) {
|
||||
sb.append(host.getEffectSource());
|
||||
} else {
|
||||
sb.append(host);
|
||||
@@ -542,7 +541,6 @@ public final class GameActionUtil {
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(sourceCard.getName() + "'s Effect");
|
||||
eff.addType("Effect");
|
||||
eff.setOwner(controller);
|
||||
|
||||
eff.setImageKey(sourceCard.getImageKey());
|
||||
|
||||
@@ -112,9 +112,9 @@ public class AbilityUtils {
|
||||
// Probably will move to One function solution sometime in the future
|
||||
public static CardCollection getDefinedCards(final Card hostCard, final String def, final CardTraitBase sa) {
|
||||
CardCollection cards = new CardCollection();
|
||||
String defined = (def == null) ? "Self" : applyAbilityTextChangeEffects(def, sa); // default to Self
|
||||
final String[] incR = defined.split("\\.", 2);
|
||||
defined = incR[0];
|
||||
String changedDef = (def == null) ? "Self" : applyAbilityTextChangeEffects(def, sa); // default to Self
|
||||
final String[] incR = changedDef.split("\\.", 2);
|
||||
String defined = incR[0];
|
||||
final Game game = hostCard.getGame();
|
||||
|
||||
Card c = null;
|
||||
@@ -132,7 +132,7 @@ public class AbilityUtils {
|
||||
}
|
||||
}
|
||||
else if (defined.equals("EffectSource")) {
|
||||
if (hostCard.isEmblem() || hostCard.getType().hasSubtype("Effect")) {
|
||||
if (hostCard.isImmutable()) {
|
||||
c = findEffectRoot(hostCard);
|
||||
}
|
||||
}
|
||||
@@ -346,6 +346,23 @@ public class AbilityUtils {
|
||||
cards.add(game.getCardState(cardByID));
|
||||
}
|
||||
}
|
||||
} else if (defined.startsWith("Valid")) {
|
||||
Iterable<Card> candidates;
|
||||
String validDefined;
|
||||
if (defined.startsWith("Valid ")) {
|
||||
candidates = game.getCardsIn(ZoneType.Battlefield);
|
||||
validDefined = changedDef.substring("Valid ".length());
|
||||
} else if (defined.startsWith("ValidAll ")) {
|
||||
candidates = game.getCardsInGame();
|
||||
validDefined = changedDef.substring("ValidAll ".length());
|
||||
} else {
|
||||
String[] s = changedDef.split(" ", 2);
|
||||
String zone = s[0].substring("Valid".length());
|
||||
candidates = game.getCardsIn(ZoneType.smartValueOf(zone));
|
||||
validDefined = s[1];
|
||||
}
|
||||
cards.addAll(CardLists.getValidCards(candidates, validDefined.split(","), hostCard.getController(), hostCard, sa));
|
||||
return cards;
|
||||
} else {
|
||||
CardCollection list = null;
|
||||
if (sa instanceof SpellAbility) {
|
||||
@@ -377,19 +394,6 @@ public class AbilityUtils {
|
||||
}
|
||||
}
|
||||
|
||||
if (defined.startsWith("Valid ")) {
|
||||
String validDefined = defined.substring("Valid ".length());
|
||||
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), validDefined.split(","), hostCard.getController(), hostCard, sa);
|
||||
} else if (defined.startsWith("ValidAll ")) {
|
||||
String validDefined = defined.substring("ValidAll ".length());
|
||||
list = CardLists.getValidCards(game.getCardsInGame(), validDefined.split(","), hostCard.getController(), hostCard, sa);
|
||||
} else if (defined.startsWith("Valid")) {
|
||||
String[] s = defined.split(" ");
|
||||
String zone = s[0].substring("Valid".length());
|
||||
String validDefined = s[1];
|
||||
list = CardLists.getValidCards(game.getCardsIn(ZoneType.smartValueOf(zone)), validDefined.split(","), hostCard.getController(), hostCard, sa);
|
||||
}
|
||||
|
||||
if (list != null) {
|
||||
cards.addAll(list);
|
||||
}
|
||||
@@ -421,7 +425,7 @@ public class AbilityUtils {
|
||||
private static Card findEffectRoot(Card startCard) {
|
||||
Card cc = startCard.getEffectSource();
|
||||
if (cc != null) {
|
||||
if (cc.isEmblem() || cc.getType().hasSubtype("Effect")) {
|
||||
if (cc.isImmutable()) {
|
||||
return findEffectRoot(cc);
|
||||
}
|
||||
return cc;
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Table;
|
||||
|
||||
import forge.GameCommand;
|
||||
import forge.card.CardType;
|
||||
import forge.card.MagicColor;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
@@ -457,25 +456,19 @@ public abstract class SpellAbilityEffect {
|
||||
final Card eff = new Card(game.nextCardId(), game);
|
||||
eff.setTimestamp(game.getNextTimestamp());
|
||||
eff.setName(name);
|
||||
eff.setColor(hostCard.determineColor().getColor());
|
||||
// if name includes emblem then it should be one
|
||||
eff.addType(name.startsWith("Emblem") ? "Emblem" : "Effect");
|
||||
// add Planeswalker types into Emblem for fun
|
||||
if (name.startsWith("Emblem") && hostCard.isPlaneswalker()) {
|
||||
for (final String type : hostCard.getType().getSubtypes()) {
|
||||
if (CardType.isAPlaneswalkerType(type)) {
|
||||
eff.addType(type);
|
||||
}
|
||||
}
|
||||
if (name.startsWith("Emblem")) {
|
||||
eff.setEmblem(true);
|
||||
// Emblem needs to be colorless
|
||||
eff.setColor(MagicColor.COLORLESS);
|
||||
}
|
||||
|
||||
eff.setOwner(controller);
|
||||
eff.setSVars(sa.getSVars());
|
||||
|
||||
eff.setImageKey(image);
|
||||
if (eff.getType().hasType(CardType.CoreType.Emblem)) {
|
||||
eff.setColor(MagicColor.COLORLESS);
|
||||
} else {
|
||||
eff.setColor(hostCard.determineColor().getColor());
|
||||
}
|
||||
|
||||
eff.setImmutable(true);
|
||||
eff.setEffectSource(sa);
|
||||
|
||||
|
||||
@@ -190,11 +190,6 @@ public class AnimateEffect extends AnimateEffectBase {
|
||||
}
|
||||
}
|
||||
|
||||
// Restore immutable to effect
|
||||
if (sa.hasParam("Immutable")) {
|
||||
c.setImmutable(true);
|
||||
}
|
||||
|
||||
game.fireEvent(new GameEventCardStatsChanged(c));
|
||||
}
|
||||
|
||||
|
||||
@@ -164,14 +164,6 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(AbilityUtils.getSVar(sa, s), c, false, sa);
|
||||
addedTriggers.add(parsedTrigger);
|
||||
}
|
||||
if (sa.hasParam("GainsTriggeredAbilitiesOf")) {
|
||||
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("GainsTriggeredAbilitiesOf"), sa);
|
||||
for (final Card card : cards) {
|
||||
for (Trigger t : card.getTriggers()) {
|
||||
addedTriggers.add(t.copy(c, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// give replacement effects
|
||||
final List<ReplacementEffect> addedReplacements = Lists.newArrayList();
|
||||
|
||||
@@ -12,7 +12,6 @@ public class DetachedCardEffect extends Card {
|
||||
card = card0;
|
||||
|
||||
setName(name0);
|
||||
addType("Effect");
|
||||
setOwner(card0.getOwner());
|
||||
setImmutable(true);
|
||||
|
||||
|
||||
@@ -140,10 +140,6 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
final Card eff = createEffect(sa, controller, name, image);
|
||||
eff.setSetCode(sa.getHostCard().getSetCode());
|
||||
eff.setRarity(sa.getHostCard().getRarity());
|
||||
// For Raging River effect to add attacker "left" or "right" pile later
|
||||
if (sa.hasParam("Mutable")) {
|
||||
eff.setImmutable(false);
|
||||
}
|
||||
|
||||
// Abilities and triggers work the same as they do for Token
|
||||
// Grant abilities
|
||||
|
||||
@@ -37,7 +37,7 @@ public class RegenerationEffect extends SpellAbilityEffect {
|
||||
// Play the Regen sound
|
||||
game.fireEvent(new GameEventCardRegenerated());
|
||||
|
||||
if (host.getType().hasStringType("Effect")) {
|
||||
if (host.isImmutable()) {
|
||||
c.subtractShield(host);
|
||||
host.removeRemembered(c);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
|
||||
prevent -= n;
|
||||
|
||||
if (!StringUtils.isNumeric(varValue) && card.getSVar(varValue).startsWith("Number$")) {
|
||||
if (card.getType().hasStringType("Effect") && prevent <= 0) {
|
||||
if (card.isImmutable() && prevent <= 0) {
|
||||
game.getAction().exile(card, null);
|
||||
} else {
|
||||
card.setSVar(varValue, "Number$" + prevent);
|
||||
|
||||
@@ -45,7 +45,7 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
|
||||
dmg -= n;
|
||||
prevent -= n;
|
||||
|
||||
if (card.getType().hasStringType("Effect") && prevent <= 0) {
|
||||
if (card.isImmutable() && prevent <= 0) {
|
||||
game.getAction().exile(card, null);
|
||||
} else if (!StringUtils.isNumeric(varValue)) {
|
||||
sa.setSVar(varValue, "Number$" + prevent);
|
||||
|
||||
@@ -31,6 +31,7 @@ import forge.game.event.GameEventCardStatsChanged;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public abstract class TokenEffectBase extends SpellAbilityEffect {
|
||||
@@ -118,7 +119,7 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
|
||||
}
|
||||
tok.setTimestamp(timestamp);
|
||||
tok.setToken(true);
|
||||
|
||||
|
||||
// do effect stuff with the token
|
||||
if (sa.hasParam("TokenTapped")) {
|
||||
tok.setTapped(true);
|
||||
@@ -138,6 +139,15 @@ public abstract class TokenEffectBase extends SpellAbilityEffect {
|
||||
tok.addEtbCounter(cType, cAmount, creator);
|
||||
}
|
||||
|
||||
if (sa.hasParam("AddTriggersFrom")) {
|
||||
final List<Card> cards = AbilityUtils.getDefinedCards(host, sa.getParam("AddTriggersFrom"), sa);
|
||||
for (final Card card : cards) {
|
||||
for (final Trigger trig : card.getTriggers()) {
|
||||
tok.addTrigger(trig.copy(tok, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clone) {
|
||||
tok.setCopiedPermanent(prototype);
|
||||
}
|
||||
|
||||
@@ -264,6 +264,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|
||||
// for Vanguard / Manapool / Emblems etc.
|
||||
private boolean isImmutable = false;
|
||||
private boolean isEmblem = false;
|
||||
|
||||
private int exertThisTurn = 0;
|
||||
private PlayerCollection exertedByPlayer = new PlayerCollection();
|
||||
@@ -4598,15 +4599,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
|
||||
public final boolean isPermanent() {
|
||||
return !isImmutable && (isInZone(ZoneType.Battlefield) || getType().isPermanent());
|
||||
return !isImmutable() && (isInZone(ZoneType.Battlefield) || getType().isPermanent());
|
||||
}
|
||||
|
||||
public final boolean isSpell() {
|
||||
return (isInstant() || isSorcery() || (isAura() && !isInZone((ZoneType.Battlefield))));
|
||||
}
|
||||
|
||||
public final boolean isEmblem() { return getType().isEmblem(); }
|
||||
|
||||
public final boolean isLand() { return getType().isLand(); }
|
||||
public final boolean isBasicLand() { return getType().isBasicLand(); }
|
||||
public final boolean isSnow() { return getType().isSnow(); }
|
||||
@@ -4865,11 +4864,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
// Takes one argument like Permanent.Blue+withFlying
|
||||
@Override
|
||||
public final boolean isValid(final String restriction, final Player sourceController, final Card source, CardTraitBase spellAbility) {
|
||||
if (isImmutable() && source != null && !source.isRemembered(this) &&
|
||||
!(restriction.startsWith("Emblem") || restriction.startsWith("Effect"))) { // special case exclusion
|
||||
return false;
|
||||
}
|
||||
|
||||
// Inclusive restrictions are Card types
|
||||
final String[] incR = restriction.split("\\.", 2);
|
||||
|
||||
@@ -4879,14 +4873,27 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
incR[0] = incR[0].substring(1); // consume negation sign
|
||||
}
|
||||
|
||||
if (incR[0].equals("Spell") && !isSpell()) {
|
||||
return testFailed;
|
||||
}
|
||||
if (incR[0].equals("Permanent") && !isPermanent()) {
|
||||
return testFailed;
|
||||
}
|
||||
if (!incR[0].equals("card") && !incR[0].equals("Card") && !incR[0].equals("Spell")
|
||||
&& !incR[0].equals("Permanent") && !getType().hasStringType(incR[0])) {
|
||||
if (incR[0].equals("Spell")) {
|
||||
if (!isSpell()) {
|
||||
return testFailed;
|
||||
}
|
||||
} else if (incR[0].equals("Permanent")) {
|
||||
if (!isPermanent()) {
|
||||
return testFailed;
|
||||
}
|
||||
} else if (incR[0].equals("Effect")) {
|
||||
if (!isImmutable()) {
|
||||
return testFailed;
|
||||
}
|
||||
} else if (incR[0].equals("Emblem")) {
|
||||
if (!isEmblem()) {
|
||||
return testFailed;
|
||||
}
|
||||
} else if (incR[0].equals("card") || incR[0].equals("Card")) {
|
||||
if (isImmutable()) {
|
||||
return testFailed;
|
||||
}
|
||||
} else if (!getType().hasStringType(incR[0])) {
|
||||
return testFailed; // Check for wrong type
|
||||
}
|
||||
|
||||
@@ -4916,6 +4923,15 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
}
|
||||
public final void setImmutable(final boolean isImmutable0) {
|
||||
isImmutable = isImmutable0;
|
||||
view.updateImmutable(this);
|
||||
}
|
||||
|
||||
public final boolean isEmblem() {
|
||||
return isEmblem;
|
||||
}
|
||||
public final void setEmblem(final boolean isEmblem0) {
|
||||
isEmblem = isEmblem0;
|
||||
view.updateEmblem(this);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -357,7 +357,7 @@ public class CardProperty {
|
||||
return false;
|
||||
}
|
||||
} else if (property.equals("EffectSource")) {
|
||||
if (!source.isEmblem() && !source.getType().hasSubtype("Effect")) {
|
||||
if (!source.isImmutable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -242,6 +242,7 @@ public final class CardUtil {
|
||||
newCopy.setToken(in.isToken());
|
||||
newCopy.setCopiedSpell(in.isCopiedSpell());
|
||||
newCopy.setImmutable(in.isImmutable());
|
||||
newCopy.setEmblem(in.isEmblem());
|
||||
|
||||
// lock in the current P/T
|
||||
newCopy.setBasePower(in.getCurrentPower());
|
||||
|
||||
@@ -233,6 +233,20 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.Token, c.isToken());
|
||||
}
|
||||
|
||||
public boolean isImmutable() {
|
||||
return get(TrackableProperty.IsImmutable);
|
||||
}
|
||||
public void updateImmutable(Card c) {
|
||||
set(TrackableProperty.IsImmutable, c.isImmutable());
|
||||
}
|
||||
|
||||
public boolean isEmblem() {
|
||||
return get(TrackableProperty.IsEmblem);
|
||||
}
|
||||
public void updateEmblem(Card c) {
|
||||
set(TrackableProperty.IsEmblem, c.isEmblem());
|
||||
}
|
||||
|
||||
public boolean isTokenCard() { return get(TrackableProperty.TokenCard); }
|
||||
void updateTokenCard(Card c) { set(TrackableProperty.TokenCard, c.isTokenCard()); }
|
||||
|
||||
|
||||
@@ -3176,6 +3176,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
if (monarchEffect == null) {
|
||||
monarchEffect = new Card(game.nextCardId(), null, game);
|
||||
monarchEffect.setOwner(this);
|
||||
monarchEffect.setImmutable(true);
|
||||
if (set != null) {
|
||||
monarchEffect.setImageKey("t:monarch_" + set.toLowerCase());
|
||||
monarchEffect.setSetCode(set);
|
||||
@@ -3183,7 +3184,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
monarchEffect.setImageKey("t:monarch");
|
||||
}
|
||||
monarchEffect.setName("The Monarch");
|
||||
monarchEffect.addType("Effect");
|
||||
|
||||
{
|
||||
final String drawTrig = "Mode$ Phase | Phase$ End of Turn | TriggerZones$ Command | " +
|
||||
@@ -3280,8 +3280,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
blessingEffect.setOwner(this);
|
||||
blessingEffect.setImageKey("t:blessing");
|
||||
blessingEffect.setName("City's Blessing");
|
||||
blessingEffect.addType("Effect");
|
||||
|
||||
blessingEffect.setImmutable(true);
|
||||
|
||||
blessingEffect.updateStateForView();
|
||||
|
||||
@@ -3353,7 +3352,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
keywordEffect.setOwner(this);
|
||||
keywordEffect.setName("Keyword Effects");
|
||||
keywordEffect.setImageKey(ImageKeys.HIDDEN_CARD);
|
||||
keywordEffect.addType("Effect");
|
||||
|
||||
keywordEffect.updateStateForView();
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
||||
if (hasParam("Description") && !this.isSuppressed()) {
|
||||
String desc = AbilityUtils.applyDescriptionTextChangeEffects(getParam("Description"), this);
|
||||
String currentName;
|
||||
if (this.isIntrinsic() && !this.getHostCard().isMutated() && cardState != null) {
|
||||
if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) {
|
||||
currentName = cardState.getName();
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -95,8 +95,7 @@ public class LandAbility extends Ability {
|
||||
Card source = sta.getHostCard();
|
||||
if (!source.equals(getHostCard())) {
|
||||
sb.append(" by ");
|
||||
if ((source.isEmblem() || source.getType().hasSubtype("Effect"))
|
||||
&& source.getEffectSource() != null) {
|
||||
if (source.isImmutable() && source.getEffectSource() != null) {
|
||||
sb.append(source.getEffectSource());
|
||||
} else {
|
||||
sb.append(source);
|
||||
|
||||
@@ -854,7 +854,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
if (node.getHostCard() != null) {
|
||||
String currentName;
|
||||
// if alternate state is viewed while card uses original
|
||||
if (node.isIntrinsic() && !node.getHostCard().isMutated() && node.cardState != null) {
|
||||
if (node.isIntrinsic() && node.cardState != null && node.cardState.getCard() == node.getHostCard()) {
|
||||
currentName = node.cardState.getName();
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -209,7 +209,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
||||
public final String toString() {
|
||||
if (hasParam("Description") && !this.isSuppressed()) {
|
||||
String currentName;
|
||||
if (this.isIntrinsic() && !this.getHostCard().isMutated() && cardState != null) {
|
||||
if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) {
|
||||
currentName = cardState.getName();
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -132,7 +132,7 @@ public abstract class Trigger extends TriggerReplacementBase {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String currentName;
|
||||
if (this.isIntrinsic() && !this.getHostCard().isMutated() && cardState != null) {
|
||||
if (this.isIntrinsic() && cardState != null && cardState.getCard() == getHostCard()) {
|
||||
currentName = cardState.getName();
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -24,6 +24,9 @@ public enum TrackableProperty {
|
||||
Controller(TrackableTypes.PlayerViewType),
|
||||
Zone(TrackableTypes.EnumType(ZoneType.class)),
|
||||
|
||||
IsImmutable(TrackableTypes.BooleanType),
|
||||
IsEmblem(TrackableTypes.BooleanType),
|
||||
|
||||
Flipped(TrackableTypes.BooleanType),
|
||||
Facedown(TrackableTypes.BooleanType),
|
||||
Foretold(TrackableTypes.BooleanType),
|
||||
|
||||
Reference in New Issue
Block a user