mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Merge master
This commit is contained in:
@@ -108,9 +108,6 @@ public class ComputerUtil {
|
|||||||
if (chooseTargets != null) {
|
if (chooseTargets != null) {
|
||||||
chooseTargets.run();
|
chooseTargets.run();
|
||||||
}
|
}
|
||||||
if (sa.isBestow()) {
|
|
||||||
sa.getHostCard().animateBestow();
|
|
||||||
}
|
|
||||||
|
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
// TODO: update mana color conversion for Daxos of Meletis
|
// TODO: update mana color conversion for Daxos of Meletis
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.card.CardTypeView;
|
import forge.card.CardTypeView;
|
||||||
@@ -14,11 +17,13 @@ import forge.game.player.Player;
|
|||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.Spell;
|
import forge.game.spellability.Spell;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.spellability.SpellAbilityPredicates;
|
||||||
import forge.game.spellability.SpellPermanent;
|
import forge.game.spellability.SpellPermanent;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -55,6 +60,21 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sa.hasParam("ValidSA")) {
|
||||||
|
final String valid[] = {sa.getParam("ValidSA")};
|
||||||
|
final Iterator<Card> itr = cards.iterator();
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
final Card c = itr.next();
|
||||||
|
final List<SpellAbility> validSA = Lists.newArrayList(Iterables.filter(AbilityUtils.getBasicSpellsFromPlayEffect(c, ai), SpellAbilityPredicates.isValid(valid, ai , c, sa)));
|
||||||
|
if (validSA.size() == 0) {
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (game.getRules().hasAppliedVariant(GameType.MoJhoSto) && source.getName().equals("Jhoira of the Ghitu Avatar")) {
|
if (game.getRules().hasAppliedVariant(GameType.MoJhoSto) && source.getName().equals("Jhoira of the Ghitu Avatar")) {
|
||||||
// Additional logic for MoJhoSto:
|
// Additional logic for MoJhoSto:
|
||||||
// Do not activate Jhoira too early, usually there are few good targets
|
// Do not activate Jhoira too early, usually there are few good targets
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.google.common.base.Predicate;
|
|||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import forge.util.CardTranslation;
|
||||||
import forge.util.ComparableOp;
|
import forge.util.ComparableOp;
|
||||||
import forge.util.PredicateString;
|
import forge.util.PredicateString;
|
||||||
|
|
||||||
@@ -352,16 +353,16 @@ public final class CardRulesPredicates {
|
|||||||
case NAME:
|
case NAME:
|
||||||
boolean otherName = false;
|
boolean otherName = false;
|
||||||
if (card.getOtherPart() != null) {
|
if (card.getOtherPart() != null) {
|
||||||
otherName = op(card.getOtherPart().getName(), this.operand);
|
otherName = (op(CardTranslation.getTranslatedName(card.getOtherPart().getName()), this.operand) || op(card.getOtherPart().getName(), this.operand));
|
||||||
}
|
}
|
||||||
return otherName || op(card.getName(), this.operand);
|
return otherName || (op(CardTranslation.getTranslatedName(card.getName()), this.operand) || op(card.getName(), this.operand));
|
||||||
case SUBTYPE:
|
case SUBTYPE:
|
||||||
shouldContain = (this.getOperator() == StringOp.CONTAINS) || (this.getOperator() == StringOp.EQUALS);
|
shouldContain = (this.getOperator() == StringOp.CONTAINS) || (this.getOperator() == StringOp.EQUALS);
|
||||||
return shouldContain == card.getType().hasSubtype(this.operand);
|
return shouldContain == card.getType().hasSubtype(this.operand);
|
||||||
case ORACLE_TEXT:
|
case ORACLE_TEXT:
|
||||||
return op(card.getOracleText(), operand);
|
return (op(CardTranslation.getTranslatedOracle(card.getName()), operand) || op(card.getOracleText(), this.operand));
|
||||||
case JOINED_TYPE:
|
case JOINED_TYPE:
|
||||||
return op(card.getType().toString(), operand);
|
return (op(CardTranslation.getTranslatedType(card.getName(), card.getType().toString()), operand) || op(card.getType().toString(), operand));
|
||||||
case COST:
|
case COST:
|
||||||
final String cost = card.getManaCost().toString();
|
final String cost = card.getManaCost().toString();
|
||||||
return op(cost, operand);
|
return op(cost, operand);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import forge.StaticData;
|
|||||||
import forge.card.CardDb;
|
import forge.card.CardDb;
|
||||||
import forge.card.CardRarity;
|
import forge.card.CardRarity;
|
||||||
import forge.card.CardRules;
|
import forge.card.CardRules;
|
||||||
|
import forge.util.CardTranslation;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
@@ -187,7 +188,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return CardTranslation.getTranslatedName(name);
|
||||||
// cannot still decide, if this "name|set" format is needed anymore
|
// cannot still decide, if this "name|set" format is needed anymore
|
||||||
// return String.format("%s|%s", name, cardSet);
|
// return String.format("%s|%s", name, cardSet);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,6 +177,12 @@ public class ForgeScript {
|
|||||||
return sa.getActivatingPlayer().equals(sourceController);
|
return sa.getActivatingPlayer().equals(sourceController);
|
||||||
} else if (property.equals("OppCtrl")) {
|
} else if (property.equals("OppCtrl")) {
|
||||||
return sa.getActivatingPlayer().isOpponentOf(sourceController);
|
return sa.getActivatingPlayer().isOpponentOf(sourceController);
|
||||||
|
} else if (property.startsWith("cmc")) {
|
||||||
|
int y = sa.getPayCosts().getTotalMana().getCMC();
|
||||||
|
int x = AbilityUtils.calculateAmount(spellAbility.getHostCard(), property.substring(5), spellAbility);
|
||||||
|
if (!Expressions.compare(y, property, x)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if (sa.getHostCard() != null) {
|
} else if (sa.getHostCard() != null) {
|
||||||
return sa.getHostCard().hasProperty(property, sourceController, source, spellAbility);
|
return sa.getHostCard().hasProperty(property, sourceController, source, spellAbility);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,14 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Card changeZoneWrapped(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause, Map<AbilityKey, Object> params) {
|
private Card changeZoneWrapped(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||||
|
// 111.11. A copy of a permanent spell becomes a token as it resolves.
|
||||||
|
// The token has the characteristics of the spell that became that token.
|
||||||
|
// The token is not “created” for the purposes of any replacement effects or triggered abilities that refer to creating a token.
|
||||||
|
if (c.isCopiedSpell() && zoneTo.is(ZoneType.Battlefield) && c.isPermanent() && cause != null && cause.isSpell() && c.equals(cause.getHostCard())) {
|
||||||
|
c.setCopiedSpell(false);
|
||||||
|
c.setToken(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (c.isCopiedSpell() || (c.isImmutable() && zoneTo.is(ZoneType.Exile))) {
|
if (c.isCopiedSpell() || (c.isImmutable() && zoneTo.is(ZoneType.Exile))) {
|
||||||
// Remove Effect from command immediately, this is essential when some replacement
|
// Remove Effect from command immediately, this is essential when some replacement
|
||||||
// effects happen during the resolving of a spellability ("the next time ..." effect)
|
// effects happen during the resolving of a spellability ("the next time ..." effect)
|
||||||
@@ -637,8 +645,11 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final Card moveToStack(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
public final Card moveToStack(final Card c, SpellAbility cause, Map<AbilityKey, Object> params) {
|
||||||
final Zone stack = game.getStackZone();
|
Card result = moveTo(game.getStackZone(), c, cause, params);
|
||||||
return moveTo(stack, c, cause, params);
|
if (cause != null && cause.isSpell() && result.equals(cause.getHostCard())) {
|
||||||
|
result.setSplitStateToPlayAbility(cause);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Card moveToGraveyard(final Card c, SpellAbility cause) {
|
public final Card moveToGraveyard(final Card c, SpellAbility cause) {
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ package forge.game.ability;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import forge.card.CardStateName;
|
||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
@@ -1820,7 +1823,11 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
public static final List<SpellAbility> getBasicSpellsFromPlayEffect(final Card tgtCard, final Player controller) {
|
public static final List<SpellAbility> getBasicSpellsFromPlayEffect(final Card tgtCard, final Player controller) {
|
||||||
List<SpellAbility> sas = new ArrayList<>();
|
List<SpellAbility> sas = new ArrayList<>();
|
||||||
for (SpellAbility s : tgtCard.getBasicSpells()) {
|
List<SpellAbility> list = Lists.newArrayList(tgtCard.getBasicSpells());
|
||||||
|
if (tgtCard.isModal()) {
|
||||||
|
list.addAll(Lists.newArrayList(tgtCard.getBasicSpells(tgtCard.getState(CardStateName.Modal))));
|
||||||
|
}
|
||||||
|
for (SpellAbility s : list) {
|
||||||
final Spell newSA = (Spell) s.copy();
|
final Spell newSA = (Spell) s.copy();
|
||||||
newSA.setActivatingPlayer(controller);
|
newSA.setActivatingPlayer(controller);
|
||||||
SpellAbilityRestriction res = new SpellAbilityRestriction();
|
SpellAbilityRestriction res = new SpellAbilityRestriction();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import forge.game.card.Card;
|
|||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
|
import forge.game.card.CardZoneTable;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
@@ -25,27 +26,27 @@ public class AttachEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
final Game game = host.getGame();
|
||||||
|
|
||||||
if (host.isAura() && sa.isSpell()) {
|
if (host.isAura() && sa.isSpell()) {
|
||||||
final Player ap = sa.getActivatingPlayer();
|
CardZoneTable table = new CardZoneTable();
|
||||||
|
host.setController(sa.getActivatingPlayer(), 0);
|
||||||
|
|
||||||
|
ZoneType previousZone = host.getZone().getZoneType();
|
||||||
|
|
||||||
// The Spell_Permanent (Auras) version of this AF needs to
|
// The Spell_Permanent (Auras) version of this AF needs to
|
||||||
// move the card into play before Attaching
|
// move the card into play before Attaching
|
||||||
|
final Card c = game.getAction().moveToPlay(host, sa);
|
||||||
host.setController(ap, 0);
|
|
||||||
|
|
||||||
// 111.11. A copy of a permanent spell becomes a token as it resolves.
|
|
||||||
// The token has the characteristics of the spell that became that token.
|
|
||||||
// The token is not “created” for the purposes of any replacement effects or triggered abilities that refer to creating a token.
|
|
||||||
if (host.isCopiedSpell()) {
|
|
||||||
host.setCopiedSpell(false);
|
|
||||||
host.setToken(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Card c = ap.getGame().getAction().moveToPlay(host, ap, sa);
|
|
||||||
sa.setHostCard(c);
|
sa.setHostCard(c);
|
||||||
|
|
||||||
|
ZoneType newZone = c.getZone().getZoneType();
|
||||||
|
if (newZone != previousZone) {
|
||||||
|
table.put(previousZone, newZone, c);
|
||||||
|
}
|
||||||
|
table.triggerChangesZoneAll(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Game game = source.getGame();
|
|
||||||
|
|
||||||
CardCollection attachments;
|
CardCollection attachments;
|
||||||
final List<GameObject> targets = getDefinedOrTargeted(sa, "Defined");
|
final List<GameObject> targets = getDefinedOrTargeted(sa, "Defined");
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ package forge.game.ability.effects;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.game.Game;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.player.Player;
|
import forge.game.card.CardZoneTable;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class PermanentEffect extends SpellAbilityEffect {
|
public class PermanentEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -18,25 +20,26 @@ public class PermanentEffect extends SpellAbilityEffect {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
Player p = sa.getActivatingPlayer();
|
|
||||||
sa.getHostCard().setController(p, 0);
|
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
final Game game = host.getGame();
|
||||||
|
CardZoneTable table = new CardZoneTable();
|
||||||
|
ZoneType previousZone = host.getZone().getZoneType();
|
||||||
|
|
||||||
// 111.11. A copy of a permanent spell becomes a token as it resolves.
|
host.setController(sa.getActivatingPlayer(), 0);
|
||||||
// The token has the characteristics of the spell that became that token.
|
|
||||||
// The token is not “created” for the purposes of any replacement effects or triggered abilities that refer to creating a token.
|
|
||||||
if (host.isCopiedSpell()) {
|
|
||||||
host.setCopiedSpell(false);
|
|
||||||
host.setToken(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Card c = p.getGame().getAction().moveToPlay(host, p, sa);
|
final Card c = game.getAction().moveToPlay(host, sa);
|
||||||
sa.setHostCard(c);
|
sa.setHostCard(c);
|
||||||
|
|
||||||
// some extra for Dashing
|
// some extra for Dashing
|
||||||
if (sa.isDash()) {
|
if (sa.isDash() && c.isInPlay()) {
|
||||||
c.setSVar("EndOfTurnLeavePlay", "Dash");
|
c.setSVar("EndOfTurnLeavePlay", "Dash");
|
||||||
registerDelayedTrigger(sa, "Hand", Lists.newArrayList(c));
|
registerDelayedTrigger(sa, "Hand", Lists.newArrayList(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZoneType newZone = c.getZone().getZoneType();
|
||||||
|
if (newZone != previousZone) {
|
||||||
|
table.put(previousZone, newZone, c);
|
||||||
|
}
|
||||||
|
table.triggerChangesZoneAll(game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -28,6 +29,7 @@ import forge.game.replacement.ReplacementHandler;
|
|||||||
import forge.game.replacement.ReplacementLayer;
|
import forge.game.replacement.ReplacementLayer;
|
||||||
import forge.game.spellability.AlternativeCost;
|
import forge.game.spellability.AlternativeCost;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.spellability.SpellAbilityPredicates;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.game.zone.Zone;
|
import forge.game.zone.Zone;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
@@ -82,7 +84,7 @@ public class PlayEffect extends SpellAbilityEffect {
|
|||||||
tgtCards = new CardCollection(
|
tgtCards = new CardCollection(
|
||||||
AbilityUtils.filterListByType(game.getCardsIn(zones), sa.getParam("Valid"), sa)
|
AbilityUtils.filterListByType(game.getCardsIn(zones), sa.getParam("Valid"), sa)
|
||||||
);
|
);
|
||||||
if ( sa.hasParam("ShowCards") ) {
|
if (sa.hasParam("ShowCards")) {
|
||||||
showCards = new CardCollection(AbilityUtils.filterListByType(game.getCardsIn(zones), sa.getParam("ShowCards"), sa));
|
showCards = new CardCollection(AbilityUtils.filterListByType(game.getCardsIn(zones), sa.getParam("ShowCards"), sa));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +104,7 @@ public class PlayEffect extends SpellAbilityEffect {
|
|||||||
final CardCollection choice = new CardCollection();
|
final CardCollection choice = new CardCollection();
|
||||||
final String num = sa.hasParam("RandomNum") ? sa.getParam("RandomNum") : "1";
|
final String num = sa.hasParam("RandomNum") ? sa.getParam("RandomNum") : "1";
|
||||||
int ncopied = AbilityUtils.calculateAmount(source, num, sa);
|
int ncopied = AbilityUtils.calculateAmount(source, num, sa);
|
||||||
while(ncopied > 0) {
|
while (ncopied > 0) {
|
||||||
final PaperCard cp = Aggregates.random(copysource);
|
final PaperCard cp = Aggregates.random(copysource);
|
||||||
final Card possibleCard = Card.fromPaperCard(cp, sa.getActivatingPlayer());
|
final Card possibleCard = Card.fromPaperCard(cp, sa.getActivatingPlayer());
|
||||||
// Need to temporarily set the Owner so the Game is set
|
// Need to temporarily set the Owner so the Game is set
|
||||||
@@ -145,6 +147,21 @@ public class PlayEffect extends SpellAbilityEffect {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sa.hasParam("ValidSA")) {
|
||||||
|
final String valid[] = {sa.getParam("ValidSA")};
|
||||||
|
final Iterator<Card> itr = tgtCards.iterator();
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
final Card c = itr.next();
|
||||||
|
final List<SpellAbility> validSA = Lists.newArrayList(Iterables.filter(AbilityUtils.getBasicSpellsFromPlayEffect(c, controller), SpellAbilityPredicates.isValid(valid, controller , c, sa)));
|
||||||
|
if (validSA.size() == 0) {
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tgtCards.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (sa.hasParam("Amount") && sa.getParam("Amount").equals("All")) {
|
if (sa.hasParam("Amount") && sa.getParam("Amount").equals("All")) {
|
||||||
amount = tgtCards.size();
|
amount = tgtCards.size();
|
||||||
}
|
}
|
||||||
@@ -214,7 +231,12 @@ public class PlayEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get basic spells (no flashback, etc.)
|
// get basic spells (no flashback, etc.)
|
||||||
final List<SpellAbility> sas = AbilityUtils.getBasicSpellsFromPlayEffect(tgtCard, controller);
|
List<SpellAbility> sas = AbilityUtils.getBasicSpellsFromPlayEffect(tgtCard, controller);
|
||||||
|
if (sa.hasParam("ValidSA")) {
|
||||||
|
final String valid[] = {sa.getParam("ValidSA")};
|
||||||
|
sas = Lists.newArrayList(Iterables.filter(sas, SpellAbilityPredicates.isValid(valid, controller , source, sa)));
|
||||||
|
}
|
||||||
|
|
||||||
if (sas.isEmpty()) {
|
if (sas.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -233,6 +255,10 @@ public class PlayEffect extends SpellAbilityEffect {
|
|||||||
// For Illusionary Mask effect
|
// For Illusionary Mask effect
|
||||||
tgtSA = CardFactoryUtil.abilityMorphDown(tgtCard);
|
tgtSA = CardFactoryUtil.abilityMorphDown(tgtCard);
|
||||||
}
|
}
|
||||||
|
// in case player canceled from choice dialog
|
||||||
|
if (tgtSA == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
final boolean noManaCost = sa.hasParam("WithoutManaCost");
|
final boolean noManaCost = sa.hasParam("WithoutManaCost");
|
||||||
if (noManaCost) {
|
if (noManaCost) {
|
||||||
|
|||||||
@@ -3,24 +3,40 @@ package forge.game.card;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
public class CardFaceView implements Serializable, Comparable<CardFaceView> {
|
public class CardFaceView implements Serializable, Comparable<CardFaceView> {
|
||||||
private String name;
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1874016432028306386L;
|
||||||
|
private String displayName;
|
||||||
|
private String oracleName;
|
||||||
|
|
||||||
public CardFaceView(String faceName) {
|
public CardFaceView(String displayName) {
|
||||||
this.name = faceName;
|
this(displayName, displayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() { return name;}
|
public CardFaceView(String displayFaceName, String oracleFaceName ) {
|
||||||
|
this.displayName = displayFaceName;
|
||||||
|
this.oracleName = oracleFaceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return displayName;}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.displayName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOracleName() { return oracleName; }
|
||||||
|
|
||||||
|
public void setOracleName(String name) {
|
||||||
|
this.oracleName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(CardFaceView o) {
|
public int compareTo(CardFaceView o) {
|
||||||
return this.getName().compareTo(o.getName());
|
return this.getOracleName().compareTo(o.getOracleName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -651,25 +651,29 @@ public class CardProperty {
|
|||||||
if ((property.endsWith("Source") || property.equals("DamagedBy")) &&
|
if ((property.endsWith("Source") || property.equals("DamagedBy")) &&
|
||||||
!card.getReceivedDamageFromThisTurn().containsKey(source)) {
|
!card.getReceivedDamageFromThisTurn().containsKey(source)) {
|
||||||
return false;
|
return false;
|
||||||
} else if (property.endsWith("Remembered")) {
|
} else {
|
||||||
boolean matched = false;
|
String prop = property.substring("DamagedBy".length());
|
||||||
for (final Object obj : source.getRemembered()) {
|
|
||||||
if (!(obj instanceof Card)) {
|
boolean found = false;
|
||||||
continue;
|
for (Card d : card.getReceivedDamageFromThisTurn().keySet()) {
|
||||||
|
if (d.isValid(prop, sourceController, source, spellAbility)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
matched |= card.getReceivedDamageFromThisTurn().containsKey(obj);
|
|
||||||
}
|
}
|
||||||
if (!matched)
|
|
||||||
return false;
|
if (!found) {
|
||||||
} else if (property.endsWith("Equipped")) {
|
for (Card d : AbilityUtils.getDefinedCards(source, prop, spellAbility)) {
|
||||||
final Card equipee = source.getEquipping();
|
if (card.getReceivedDamageFromThisTurn().containsKey(d)) {
|
||||||
if (equipee == null || !card.getReceivedDamageFromThisTurn().containsKey(equipee))
|
found = true;
|
||||||
return false;
|
break;
|
||||||
} else if (property.endsWith("Enchanted")) {
|
}
|
||||||
final Card equipee = source.getEnchantingCard();
|
}
|
||||||
if (equipee == null || !card.getReceivedDamageFromThisTurn().containsKey(equipee))
|
}
|
||||||
|
if (!found) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (property.startsWith("Damaged")) {
|
} else if (property.startsWith("Damaged")) {
|
||||||
if (!card.getDealtDamageToThisTurn().containsKey(source)) {
|
if (!card.getDealtDamageToThisTurn().containsKey(source)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1035,17 +1039,21 @@ public class CardProperty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else if (property.startsWith("ThisTurnEntered")) {
|
} else if (property.equals("ThisTurnEntered")) {
|
||||||
final String restrictions = property.split("ThisTurnEntered_")[1];
|
// only check if it entered the Zone this turn
|
||||||
final String[] res = restrictions.split("_");
|
if (card.getTurnInZone() != game.getPhaseHandler().getTurn()) {
|
||||||
final ZoneType destination = ZoneType.smartValueOf(res[0]);
|
return false;
|
||||||
ZoneType origin = null;
|
|
||||||
if (res.length > 1 && res[1].equals("from")) {
|
|
||||||
origin = ZoneType.smartValueOf(res[2]);
|
|
||||||
}
|
}
|
||||||
List<Card> cards = CardUtil.getThisTurnEntered(destination,
|
} else if (property.startsWith("ThisTurnEnteredFrom")) {
|
||||||
origin, "Card", source);
|
final String restrictions = property.split("ThisTurnEnteredFrom_")[1];
|
||||||
if (!cards.contains(card)) {
|
final String[] res = restrictions.split("_");
|
||||||
|
final ZoneType origin = ZoneType.smartValueOf(res[0]);
|
||||||
|
|
||||||
|
if (card.getTurnInZone() != game.getPhaseHandler().getTurn()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!card.getZone().isCardAddedThisTurn(card, origin)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (property.equals("DiscardedThisTurn")) {
|
} else if (property.equals("DiscardedThisTurn")) {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import forge.util.Lang;
|
|||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollectionView;
|
||||||
|
import forge.util.CardTranslation;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@@ -933,7 +934,7 @@ public class CardView extends GameEntityView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (zone + ' ' + name + " (" + getId() + ")").trim();
|
return (zone + ' ' + CardTranslation.getTranslatedName(name) + " (" + getId() + ")").trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CardStateView extends TrackableObject {
|
public class CardStateView extends TrackableObject {
|
||||||
|
|||||||
@@ -211,6 +211,13 @@ public class Zone implements java.io.Serializable, Iterable<Card> {
|
|||||||
return getCardsAdded(cardsAddedLastTurn, origin);
|
return getCardsAdded(cardsAddedLastTurn, origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean isCardAddedThisTurn(final Card card, final ZoneType origin) {
|
||||||
|
if (!cardsAddedThisTurn.containsKey(origin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return cardsAddedThisTurn.get(origin).contains(card);
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Card> getCardsAdded(final MapOfLists<ZoneType, Card> cardsAdded, final ZoneType origin) {
|
private static List<Card> getCardsAdded(final MapOfLists<ZoneType, Card> cardsAdded, final ZoneType origin) {
|
||||||
if (origin != null) {
|
if (origin != null) {
|
||||||
final Collection<Card> cards = cardsAdded.get(origin);
|
final Collection<Card> cards = cardsAdded.get(origin);
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ public class GuiChoose {
|
|||||||
if (sel instanceof ICardFace) {
|
if (sel instanceof ICardFace) {
|
||||||
faceName = ((ICardFace) sel).getName();
|
faceName = ((ICardFace) sel).getName();
|
||||||
} else {
|
} else {
|
||||||
faceName = ((CardFaceView) sel).getName();
|
faceName = ((CardFaceView) sel).getOracleName();
|
||||||
}
|
}
|
||||||
PaperCard paper = FModel.getMagicDb().getCommonCards().getUniqueByName(faceName);
|
PaperCard paper = FModel.getMagicDb().getCommonCards().getUniqueByName(faceName);
|
||||||
if (paper == null) {
|
if (paper == null) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#Add one announcement per line
|
#Add one announcement per line
|
||||||
Keyboard shortcut change: to help improve accessibility, the arrow key shortcuts to move between deck editor tables now require control or Command, depending on which platform you're on.
|
Sorry for the long wait between releases. Pandemic + Technology issues caused some issues we couldn't hurdle.
|
||||||
Jumpstart sealed!
|
Get in the discord if you aren't yet.
|
||||||
Double Masters (including double pick)
|
Lots of new cards including Kaldheim.
|
||||||
Lots of art updates and fixes
|
|
||||||
No update on Mutate yet. Trust us, you'll know when it's here.
|
No update on Mutate yet. Trust us, you'll know when it's here.
|
||||||
8
forge-gui/res/cardsfolder/a/aegar_the_freezing_flame.txt
Normal file
8
forge-gui/res/cardsfolder/a/aegar_the_freezing_flame.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Name:Aegar, the Freezing Flame
|
||||||
|
ManaCost:1 U R
|
||||||
|
Types:Legendary Creature Giant Wizard
|
||||||
|
PT:3/3
|
||||||
|
T:Mode$ ExcessDamage | ValidTarget$ Creature.OppCtrl+DamagedByGiant.YouCtrl,Creature.OppCtrl+DamagedByWizard.YouCtrl,Creature.OppCtrl+DamagedBySpell.YouCtrl,Planeswalker.OppCtrl+DamagedByGiant.YouCtrl,Planeswalker.OppCtrl+DamagedByWizard.YouCtrl,Planeswalker.OppCtrl+DamagedBySpell.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever a creature or planeswalker an opponent controls is dealt excess damage, if a Giant, Wizard, or spell you controlled dealt damage to it this turn, draw a card.
|
||||||
|
SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
|
||||||
|
Oracle:Whenever a creature or planeswalker an opponent controls is dealt excess damage, if a Giant, Wizard, or spell you controlled dealt damage to it this turn, draw a card.
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@ Name:Bag of Holding
|
|||||||
ManaCost:1
|
ManaCost:1
|
||||||
Types:Artifact
|
Types:Artifact
|
||||||
T:Mode$ Discarded | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigExile | TriggerDescription$ Whenever you discard a card, exile that card from your graveyard.
|
T:Mode$ Discarded | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigExile | TriggerDescription$ Whenever you discard a card, exile that card from your graveyard.
|
||||||
SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCardLKICopy | Origin$ Graveyard | Destination$ Exile
|
SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Exile
|
||||||
A:AB$ Draw | Cost$ 2 T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard
|
A:AB$ Draw | Cost$ 2 T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard
|
||||||
SVar:DBDiscard:DB$Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
|
SVar:DBDiscard:DB$Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Baral's Expertise
|
Name:Baral's Expertise
|
||||||
ManaCost:3 U U
|
ManaCost:3 U U
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ ChangeZone | Cost$ 3 U U | TargetMin$ 0 | TargetMax$ 3 | ValidTgts$ Artifact,Creature | TgtPrompt$ Select target artifact or creature | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBPlay | SpellDescription$ Return up to three target artifacts and/or creatures to their owners' hands. You may cast a card with converted mana cost 4 or less from your hand without paying its mana cost.
|
A:SP$ ChangeZone | Cost$ 3 U U | TargetMin$ 0 | TargetMax$ 3 | ValidTgts$ Artifact,Creature | TgtPrompt$ Select target artifact or creature | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBPlay | SpellDescription$ Return up to three target artifacts and/or creatures to their owners' hands. You may cast a spell with converted mana cost 4 or less from your hand without paying its mana cost.
|
||||||
SVar:DBPlay:DB$ Play | Valid$ Card.nonLand+YouOwn+cmcLE4 | ValidZone$ Hand | WithoutManaCost$ True | Amount$ 1 | Controller$ You | Optional$ True
|
SVar:DBPlay:DB$ Play | Valid$ Card.nonLand+YouOwn | ValidSA$ Spell.cmcLE4 | ValidZone$ Hand | WithoutManaCost$ True | Amount$ 1 | Controller$ You | Optional$ True
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/barals_expertise.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/barals_expertise.jpg
|
||||||
Oracle:Return up to three target artifacts and/or creatures to their owners' hands.\nYou may cast a card with converted mana cost 4 or less from your hand without paying its mana cost.
|
Oracle:Return up to three target artifacts and/or creatures to their owners' hands.\nYou may cast a spell with converted mana cost 4 or less from your hand without paying its mana cost.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
Name:Brought Back
|
Name:Brought Back
|
||||||
ManaCost:W W
|
ManaCost:W W
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ ChangeZone | Cost$ W W | ValidTgts$ Permanent.YouOwn+ThisTurnEntered_Graveyard_from_Battlefield | TgtPrompt$ Select up to two target permanent cards in your graveyard that were put there from the battlefield this turn | TargetMin$ 0 | TargetMax$ 2 | Origin$ Graveyard | Destination$ Battlefield | Tapped$ True | SpellDescription$ Choose up to two target permanent cards in your graveyard that were put there from the battlefield this turn. Return them to the battlefield tapped.
|
A:SP$ ChangeZone | Cost$ W W | ValidTgts$ Permanent.YouOwn+ThisTurnEnteredFrom_Battlefield | TgtPrompt$ Select up to two target permanent cards in your graveyard that were put there from the battlefield this turn | TargetMin$ 0 | TargetMax$ 2 | Origin$ Graveyard | Destination$ Battlefield | Tapped$ True | SpellDescription$ Choose up to two target permanent cards in your graveyard that were put there from the battlefield this turn. Return them to the battlefield tapped.
|
||||||
Oracle:Choose up to two target permanent cards in your graveyard that were put there from the battlefield this turn. Return them to the battlefield tapped.
|
Oracle:Choose up to two target permanent cards in your graveyard that were put there from the battlefield this turn. Return them to the battlefield tapped.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Sorcery
|
|||||||
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | References$ X | Description$ This spell costs {1} less to cast for each creature in your party.
|
S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | References$ X | Description$ This spell costs {1} less to cast for each creature in your party.
|
||||||
SVar:X:Count$Party
|
SVar:X:Count$Party
|
||||||
A:SP$ ChangeZone | Cost$ 4 B | Origin$ Library | Destination$ Hand | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | SubAbility$ DBPlay | StackDescription$ SpellDescription | SpellDescription$ Search your library for a card, put it into your hand, then shuffle your library. If you have a full party, you may cast a spell with converted mana cost 4 or less from your hand without paying its mana cost.
|
A:SP$ ChangeZone | Cost$ 4 B | Origin$ Library | Destination$ Hand | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | SubAbility$ DBPlay | StackDescription$ SpellDescription | SpellDescription$ Search your library for a card, put it into your hand, then shuffle your library. If you have a full party, you may cast a spell with converted mana cost 4 or less from your hand without paying its mana cost.
|
||||||
SVar:DBPlay:DB$ Play | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ4 | References$ X | Optional$ True | Valid$ Card.nonLand+YouOwn+cmcLE4 | ValidZone$ Hand | WithoutManaCost$ True | Amount$ 1 | StackDescription$ None
|
SVar:DBPlay:DB$ Play | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ4 | References$ X | Optional$ True | Valid$ Card.nonLand+YouOwn | ValidSA$ Spell.cmcLE4 | ValidZone$ Hand | WithoutManaCost$ True | Amount$ 1 | StackDescription$ None
|
||||||
DeckHas:Ability$Party
|
DeckHas:Ability$Party
|
||||||
DeckHints:Type$Cleric|Rogue|Warrior|Wizard
|
DeckHints:Type$Cleric|Rogue|Warrior|Wizard
|
||||||
Oracle:This spell costs {1} less to cast for each creature in your party. (Your party consists of up to one each of Cleric, Rogue, Warrior, and Wizard.)\nSearch your library for a card, put it into your hand, then shuffle your library. If you have a full party, you may cast a spell with converted mana cost 4 or less from your hand without paying its mana cost.
|
Oracle:This spell costs {1} less to cast for each creature in your party. (Your party consists of up to one each of Cleric, Rogue, Warrior, and Wizard.)\nSearch your library for a card, put it into your hand, then shuffle your library. If you have a full party, you may cast a spell with converted mana cost 4 or less from your hand without paying its mana cost.
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ Name:Cry of the Carnarium
|
|||||||
ManaCost:1 B B
|
ManaCost:1 B B
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ PumpAll | Cost$ 1 B B | ValidCards$ Creature | NumAtt$ -2 | NumDef$ -2 | IsCurse$ True | ReplaceDyingValid$ Creature | SubAbility$ ExileSomeCreatures | SpellDescription$ All creatures get -2/-2 until end of turn. Exile all creature cards in all graveyards that were put there from the battlefield this turn. If a creature would die this turn, exile it instead.
|
A:SP$ PumpAll | Cost$ 1 B B | ValidCards$ Creature | NumAtt$ -2 | NumDef$ -2 | IsCurse$ True | ReplaceDyingValid$ Creature | SubAbility$ ExileSomeCreatures | SpellDescription$ All creatures get -2/-2 until end of turn. Exile all creature cards in all graveyards that were put there from the battlefield this turn. If a creature would die this turn, exile it instead.
|
||||||
SVar:ExileSomeCreatures:DB$ ChangeZone | Defined$ ThisTurnEntered_Graveyard_from_Battlefield_Creature.nonToken | Origin$ Graveyard | Destination$ Exile | SpellDescription$ Exile all creature cards in all graveyards that were put there from the battlefield this turn.
|
SVar:ExileSomeCreatures:DB$ ChangeZone | Defined$ ValidGraveyard Creature.nonToken+ThisTurnEnteredFrom_Battlefield | Origin$ Graveyard | Destination$ Exile | SpellDescription$ Exile all creature cards in all graveyards that were put there from the battlefield this turn.
|
||||||
Oracle:All creatures get -2/-2 until end of turn. Exile all creature cards in all graveyards that were put there from the battlefield this turn. If a creature would die this turn, exile it instead.
|
Oracle:All creatures get -2/-2 until end of turn. Exile all creature cards in all graveyards that were put there from the battlefield this turn. If a creature would die this turn, exile 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