mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master
This commit is contained in:
@@ -807,6 +807,8 @@ public class AbilityUtils {
|
||||
final SpellAbility root = sa.getRootAbility();
|
||||
list = new CardCollection((Card) root.getReplacingObject(AbilityKey.fromString(calcX[0].substring(8))));
|
||||
}
|
||||
// there could be null inside!
|
||||
list = Iterables.filter(list, Card.class);
|
||||
if (list != null) {
|
||||
val = handlePaid(list, calcX[1], card, ability);
|
||||
}
|
||||
@@ -3424,7 +3426,6 @@ public class AbilityUtils {
|
||||
} else {
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (string.startsWith("DifferentCMC")) {
|
||||
@@ -3437,14 +3438,13 @@ public class AbilityUtils {
|
||||
|
||||
if (string.startsWith("SumCMC")) {
|
||||
int sumCMC = 0;
|
||||
for(Card c : paidList) {
|
||||
for (Card c : paidList) {
|
||||
sumCMC += c.getCMC();
|
||||
}
|
||||
return sumCMC;
|
||||
}
|
||||
|
||||
if (string.startsWith("Valid")) {
|
||||
|
||||
final String[] splitString = string.split("/", 2);
|
||||
String valid = splitString[0].substring(6);
|
||||
final List<Card> list = CardLists.getValidCardsAsList(paidList, valid, source.getController(), source, ctb);
|
||||
@@ -3464,6 +3464,7 @@ public class AbilityUtils {
|
||||
}
|
||||
|
||||
int tot = 0;
|
||||
|
||||
for (final Card c : filteredList) {
|
||||
tot += xCount(c, filteredString, ctb);
|
||||
}
|
||||
|
||||
@@ -704,8 +704,8 @@ public abstract class SpellAbilityEffect {
|
||||
Card host = sa.getHostCard();
|
||||
final Game game = host.getGame();
|
||||
final String duration = sa.getParam("Duration");
|
||||
// in case host was LKI
|
||||
if (host.isLKI()) {
|
||||
// in case host was LKI or still resolving
|
||||
if (host.isLKI() || host.getZone().is(ZoneType.Stack)) {
|
||||
host = game.getCardState(host);
|
||||
}
|
||||
|
||||
|
||||
@@ -243,5 +243,4 @@ public class CharmEffect extends SpellAbilityEffect {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -62,6 +63,7 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
||||
|
||||
boolean randomChoice = sa.hasParam("AtRandom");
|
||||
boolean chooseFromDefined = sa.hasParam("ChooseFromDefinedCards");
|
||||
boolean chooseFromOneTimeList = sa.hasParam("ChooseFromOneTimeList");
|
||||
|
||||
if (!randomChoice) {
|
||||
if (sa.hasParam("SelectPrompt")) {
|
||||
@@ -100,7 +102,7 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
||||
} else if (chooseFromDefined) {
|
||||
CardCollection choices = AbilityUtils.getDefinedCards(host, sa.getParam("ChooseFromDefinedCards"), sa);
|
||||
choices = CardLists.getValidCards(choices, valid, host.getController(), host, sa);
|
||||
List<ICardFace> faces = Lists.newArrayList();
|
||||
List<ICardFace> faces = new ArrayList<>();
|
||||
// get Card
|
||||
for (final Card c : choices) {
|
||||
final CardRules rules = c.getRules();
|
||||
@@ -114,6 +116,22 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
||||
}
|
||||
Collections.sort(faces);
|
||||
chosen = p.getController().chooseCardName(sa, faces, message);
|
||||
} else if (chooseFromOneTimeList) {
|
||||
String [] names = sa.getParam("ChooseFromOneTimeList").split(",");
|
||||
List<ICardFace> faces = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
faces.add(StaticData.instance().getCommonCards().getFaceByName(name));
|
||||
}
|
||||
chosen = p.getController().chooseCardName(sa, faces, message);
|
||||
|
||||
// Remove chosen Name from List
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String name : names) {
|
||||
if (chosen.equals(name)) continue;
|
||||
if (sb.length() > 0) sb.append(',');
|
||||
sb.append(name);
|
||||
}
|
||||
sa.putParam("ChooseFromOneTimeList", sb.toString());
|
||||
} else {
|
||||
// use CardFace because you might name a alternate names
|
||||
Predicate<ICardFace> cpp = Predicates.alwaysTrue();
|
||||
|
||||
@@ -99,9 +99,9 @@ public class CopyPermanentEffect extends TokenEffectBase {
|
||||
if (sa.hasParam("RandomCopied")) {
|
||||
List<PaperCard> copysource = Lists.newArrayList(cards);
|
||||
List<Card> choice = Lists.newArrayList();
|
||||
final String num = sa.hasParam("RandomNum") ? sa.getParam("RandomNum") : "1";
|
||||
final String num = sa.getParamOrDefault("RandomNum","1");
|
||||
int ncopied = AbilityUtils.calculateAmount(host, num, sa);
|
||||
while(ncopied > 0 && !copysource.isEmpty()) {
|
||||
while (ncopied > 0 && !copysource.isEmpty()) {
|
||||
final PaperCard cp = Aggregates.random(copysource);
|
||||
Card possibleCard = Card.fromPaperCard(cp, activator); // Need to temporarily set the Owner so the Game is set
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ public class MustAttackEffect extends SpellAbilityEffect {
|
||||
final Card host = sa.getHostCard();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
|
||||
// end standard pre-
|
||||
|
||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||
@@ -67,7 +66,6 @@ public class MustAttackEffect extends SpellAbilityEffect {
|
||||
entity = defPWs.getFirst();
|
||||
}
|
||||
}
|
||||
//System.out.println("Setting mustAttackEntity to: "+entity);
|
||||
|
||||
for (final Player p : tgtPlayers) {
|
||||
if ((tgt == null) || p.canBeTargetedBy(sa)) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
|
||||
@@ -93,8 +94,7 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
if (sa.hasParam("ShowCards")) {
|
||||
showCards = new CardCollection(AbilityUtils.filterListByType(game.getCardsIn(zones), sa.getParam("ShowCards"), sa));
|
||||
}
|
||||
}
|
||||
else if (sa.hasParam("AnySupportedCard")) {
|
||||
} else if (sa.hasParam("AnySupportedCard")) {
|
||||
final String valid = sa.getParam("AnySupportedCard");
|
||||
List<PaperCard> cards = null;
|
||||
if (valid.startsWith("Names:")){
|
||||
@@ -139,8 +139,14 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else if (sa.hasParam("CopyFromChosenName")) {
|
||||
String name = source.getChosenName();
|
||||
if (name.trim().isEmpty()) return;
|
||||
Card card = Card.fromPaperCard(StaticData.instance().getCommonCards().getUniqueByName(name), controller);
|
||||
card.setToken(true);
|
||||
tgtCards = new CardCollection();
|
||||
tgtCards.add(card);
|
||||
} else {
|
||||
tgtCards = new CardCollection();
|
||||
// filter only cards that didn't changed zones
|
||||
for (Card c : getTargetCards(sa)) {
|
||||
@@ -179,7 +185,16 @@ public class PlayEffect extends SpellAbilityEffect {
|
||||
|
||||
while (!tgtCards.isEmpty() && amount > 0) {
|
||||
activator.getController().tempShowCards(showCards);
|
||||
Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), optional, null);
|
||||
Card tgtCard = null;
|
||||
if (tgtCards.size() == 1 && amount == 1 && optional) {
|
||||
tgtCard = tgtCards.get(0);
|
||||
if (!controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantPlayCard", CardTranslation.getTranslatedName(tgtCard.getName())))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), optional, null);
|
||||
}
|
||||
|
||||
activator.getController().endTempShowCards();
|
||||
if (tgtCard == null) {
|
||||
break;
|
||||
|
||||
@@ -3,6 +3,7 @@ package forge.game.ability.effects;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.GameCommand;
|
||||
@@ -154,20 +155,27 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
||||
game.getUntap().addUntil(p, new GameCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<Object> tempRemembered = Lists.newArrayList(Iterables.filter(source.getRemembered(), Player.class));
|
||||
source.removeRemembered(tempRemembered);
|
||||
source.addRemembered(p);
|
||||
AbilityUtils.resolve(repeat);
|
||||
source.removeRemembered(p);
|
||||
source.addRemembered(tempRemembered);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// to avoid risk of collision with other abilities swap out other Remembered Player while resolving
|
||||
List<Object> tempRemembered = Lists.newArrayList(Iterables.filter(source.getRemembered(), Player.class));
|
||||
source.removeRemembered(tempRemembered);
|
||||
source.addRemembered(p);
|
||||
AbilityUtils.resolve(repeat);
|
||||
source.removeRemembered(p);
|
||||
source.addRemembered(tempRemembered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sa.hasParam("DamageMap")) {
|
||||
if (sa.hasParam("DamageMap")) {
|
||||
game.getAction().dealDamage(false, sa.getDamageMap(), sa.getPreventMap(), sa.getCounterTable(), sa);
|
||||
}
|
||||
if (sa.hasParam("ChangeZoneTable")) {
|
||||
|
||||
@@ -82,7 +82,7 @@ public class ReplaceManaEffect extends SpellAbilityEffect {
|
||||
|
||||
// need to log Updated events there, or the log is wrong order
|
||||
String message = sa.getReplacementEffect().toString();
|
||||
if ( !StringUtils.isEmpty(message)) {
|
||||
if (!StringUtils.isEmpty(message)) {
|
||||
message = TextUtil.fastReplace(message, "CARDNAME", card.getName());
|
||||
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
|
||||
}
|
||||
|
||||
@@ -1708,6 +1708,9 @@ public class CardProperty {
|
||||
}
|
||||
} else if (property.startsWith("set")) {
|
||||
final String setCode = property.substring(3, 6);
|
||||
if (card.getName().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final PaperCard setCard = StaticData.instance().getCommonCards().getCardFromEdition(card.getName(), CardDb.SetPreference.Earliest);
|
||||
if (setCard != null && !setCard.getEdition().equals(setCode)) {
|
||||
return false;
|
||||
|
||||
@@ -1086,7 +1086,7 @@ public class CardView extends GameEntityView {
|
||||
}
|
||||
|
||||
public CardTypeView getType() {
|
||||
if (isFaceDown() && !isInZone(EnumSet.of(ZoneType.Battlefield, ZoneType.Stack))) {
|
||||
if (getState() != CardStateName.Original && isFaceDown() && !isInZone(EnumSet.of(ZoneType.Battlefield, ZoneType.Stack))) {
|
||||
return CardType.EMPTY;
|
||||
}
|
||||
return get(TrackableProperty.Type);
|
||||
|
||||
@@ -31,6 +31,7 @@ import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostParser;
|
||||
import forge.game.CardTraitBase;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.player.Player;
|
||||
@@ -876,15 +877,30 @@ public class Cost implements Serializable {
|
||||
costParts.add(0, new CostPartMana(oldManaCost.toManaCost(), r));
|
||||
}
|
||||
} else if (part instanceof CostDiscard || part instanceof CostTapType ||
|
||||
part instanceof CostAddMana || part instanceof CostPayLife) {
|
||||
part instanceof CostAddMana || part instanceof CostPayLife
|
||||
|| part instanceof CostPutCounter) {
|
||||
boolean alreadyAdded = false;
|
||||
for (final CostPart other : costParts) {
|
||||
if (other.getClass().equals(part.getClass()) &&
|
||||
if ((other.getClass().equals(part.getClass()) || (part instanceof CostPutCounter && ((CostPutCounter)part).getCounter().is(CounterEnumType.LOYALTY))) &&
|
||||
part.getType().equals(other.getType()) &&
|
||||
StringUtils.isNumeric(part.getAmount()) &&
|
||||
StringUtils.isNumeric(other.getAmount())) {
|
||||
final String amount = String.valueOf(Integer.parseInt(part.getAmount()) + Integer.parseInt(other.getAmount()));
|
||||
if (part instanceof CostDiscard) {
|
||||
String amount = String.valueOf(Integer.parseInt(part.getAmount()) + Integer.parseInt(other.getAmount()));
|
||||
if (part instanceof CostPutCounter) { // path for Carth
|
||||
if (other instanceof CostPutCounter && ((CostPutCounter)other).getCounter().is(CounterEnumType.LOYALTY)) {
|
||||
costParts.add(new CostPutCounter(amount, CounterType.get(CounterEnumType.LOYALTY), part.getType(), part.getTypeDescription()));
|
||||
} else if (other instanceof CostRemoveCounter && ((CostRemoveCounter)other).counter.is(CounterEnumType.LOYALTY)) {
|
||||
Integer counters = Integer.parseInt(other.getAmount()) - Integer.parseInt(part.getAmount());
|
||||
// the cost can turn positive if multiple Carth raise it
|
||||
if (counters < 0) {
|
||||
costParts.add(new CostPutCounter(String.valueOf(counters *-1), CounterType.get(CounterEnumType.LOYALTY), part.getType(), part.getTypeDescription()));
|
||||
} else {
|
||||
costParts.add(new CostRemoveCounter(String.valueOf(counters), CounterType.get(CounterEnumType.LOYALTY), part.getType(), part.getTypeDescription(), ZoneType.Battlefield));
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (part instanceof CostDiscard) {
|
||||
costParts.add(new CostDiscard(amount, part.getType(), part.getTypeDescription()));
|
||||
} else if (part instanceof CostTapType) {
|
||||
CostTapType tappart = (CostTapType)part;
|
||||
|
||||
@@ -86,11 +86,12 @@ public class CostPayment extends ManaConversionMatrix {
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean canPayAdditionalCosts(final Cost cost, final SpellAbility ability) {
|
||||
public static boolean canPayAdditionalCosts(Cost cost, final SpellAbility ability) {
|
||||
if (cost == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cost = CostAdjustment.adjust(cost, ability);
|
||||
return cost.canPay(ability);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ public class CostPutCounter extends CostPartWithList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int paymentOrder() { return 8; }
|
||||
public int paymentOrder() { return 6; }
|
||||
|
||||
@Override
|
||||
public boolean isReusable() {
|
||||
|
||||
@@ -1762,7 +1762,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
public final boolean playLand(final Card land, final boolean ignoreZoneAndTiming) {
|
||||
// Dakkon Blackblade Avatar will use a similar effect
|
||||
if (canPlayLand(land, ignoreZoneAndTiming)) {
|
||||
this.playLandNoCheck(land);
|
||||
playLandNoCheck(land);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1775,8 +1775,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
if (land.isFaceDown()) {
|
||||
land.turnFaceUp(null);
|
||||
}
|
||||
final Card c = game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null);
|
||||
game.copyLastState();
|
||||
final Card c = game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null);
|
||||
game.updateLastStateForCard(c);
|
||||
|
||||
// play a sound
|
||||
|
||||
@@ -31,7 +31,6 @@ public class ReplaceProduceMana extends ReplacementEffect {
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Affected))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -765,6 +765,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
resetTriggeringObjects();
|
||||
resetTriggerRemembered();
|
||||
|
||||
if (isActivatedAbility()) {
|
||||
setXManaCostPaid(null);
|
||||
}
|
||||
|
||||
// reset last state when finished resolving
|
||||
setLastStateBattlefield(CardCollection.EMPTY);
|
||||
setLastStateGraveyard(CardCollection.EMPTY);
|
||||
|
||||
@@ -195,7 +195,6 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean checkZoneRestrictions(final Card c, final SpellAbility sa) {
|
||||
|
||||
final Player activator = sa.getActivatingPlayer();
|
||||
final Zone cardZone = c.getLastKnownZone();
|
||||
Card cp = c;
|
||||
|
||||
@@ -366,7 +366,6 @@ public class TargetRestrictions {
|
||||
return this.saValidTargeting;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* canOnlyTgtOpponent.
|
||||
|
||||
@@ -57,6 +57,9 @@ public class StaticAbilityCantBeCast {
|
||||
}
|
||||
|
||||
public static boolean cantBeActivatedAbility(final SpellAbility spell, final Card card, final Player activator) {
|
||||
if (spell.isTrigger()) {
|
||||
return false;
|
||||
}
|
||||
final Game game = activator.getGame();
|
||||
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
@@ -98,7 +101,6 @@ public class StaticAbilityCantBeCast {
|
||||
* @return true, if successful
|
||||
*/
|
||||
public static boolean applyCantBeCastAbility(final StaticAbility stAb, final SpellAbility spell, final Card card, final Player activator) {
|
||||
|
||||
if (!stAb.matchesValidParam("ValidCard", card)) {
|
||||
return false;
|
||||
}
|
||||
@@ -153,7 +155,6 @@ public class StaticAbilityCantBeCast {
|
||||
* @return true, if successful
|
||||
*/
|
||||
public static boolean applyCantBeActivatedAbility(final StaticAbility stAb, final SpellAbility spellAbility, final Card card, final Player activator) {
|
||||
|
||||
if (!stAb.matchesValidParam("ValidCard", card)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -543,7 +543,6 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
}
|
||||
|
||||
private final void finishResolving(final SpellAbility sa, final boolean fizzle) {
|
||||
|
||||
// SpellAbility is removed from the stack here
|
||||
// temporarily removed removing SA after resolution
|
||||
final SpellAbilityStackInstance si = getInstanceFromSpellAbility(sa);
|
||||
|
||||
Reference in New Issue
Block a user