mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Merge branch 'master' into boosterQoL
This commit is contained in:
@@ -25,7 +25,7 @@ This file is tarball, and may need to be extracted twice depending on which prog
|
|||||||
We recommend extracting to a new folder rather than on top of an existing installation.
|
We recommend extracting to a new folder rather than on top of an existing installation.
|
||||||
**For users who have played Forge before all of your user data is stored separately so you don't have to worry about losing it on upgrade.**
|
**For users who have played Forge before all of your user data is stored separately so you don't have to worry about losing it on upgrade.**
|
||||||
|
|
||||||
Java 8 or later is required to run Forge. Please make sure is the right version is installed in your enviroment. Check the user guide for more info.
|
Java 8 or later is required to run Forge. Please make sure is the right version is installed in your environment. Check the user guide for more info.
|
||||||
|
|
||||||
For Android users, download the APK file from [Snapshot Build](https://downloads.cardforge.org/dailysnapshots/) to your device.
|
For Android users, download the APK file from [Snapshot Build](https://downloads.cardforge.org/dailysnapshots/) to your device.
|
||||||
On first run, Forge will download all needed data.
|
On first run, Forge will download all needed data.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-ai</artifactId>
|
<artifactId>forge-ai</artifactId>
|
||||||
|
|||||||
@@ -923,6 +923,10 @@ public class AiController {
|
|||||||
if (checkCurseEffects(sa)) {
|
if (checkCurseEffects(sa)) {
|
||||||
return AiPlayDecision.CurseEffects;
|
return AiPlayDecision.CurseEffects;
|
||||||
}
|
}
|
||||||
|
// TODO maybe other location for this?
|
||||||
|
if (!sa.isLegalAfterStack()) {
|
||||||
|
return AiPlayDecision.AnotherTime;
|
||||||
|
}
|
||||||
Card spellHost = card;
|
Card spellHost = card;
|
||||||
if (sa.isSpell()) {
|
if (sa.isSpell()) {
|
||||||
spellHost = CardCopyService.getLKICopy(spellHost);
|
spellHost = CardCopyService.getLKICopy(spellHost);
|
||||||
@@ -930,10 +934,6 @@ public class AiController {
|
|||||||
spellHost.setLastKnownZone(game.getStackZone()); // need to add to stack to make check Restrictions respect stack cmc
|
spellHost.setLastKnownZone(game.getStackZone()); // need to add to stack to make check Restrictions respect stack cmc
|
||||||
spellHost.setCastFrom(card.getZone());
|
spellHost.setCastFrom(card.getZone());
|
||||||
}
|
}
|
||||||
// TODO maybe other location for this?
|
|
||||||
if (!sa.isLegalAfterStack()) {
|
|
||||||
return AiPlayDecision.AnotherTime;
|
|
||||||
}
|
|
||||||
if (!sa.checkRestrictions(spellHost, player)) {
|
if (!sa.checkRestrictions(spellHost, player)) {
|
||||||
return AiPlayDecision.AnotherTime;
|
return AiPlayDecision.AnotherTime;
|
||||||
}
|
}
|
||||||
@@ -946,7 +946,7 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sa instanceof Spell) {
|
if (sa instanceof Spell) {
|
||||||
if (card.isPermanent()) {
|
if (sa.getApi() == ApiType.PermanentCreature || sa.getApi() == ApiType.PermanentNoncreature) {
|
||||||
return canPlayFromEffectAI((Spell) sa, false, true);
|
return canPlayFromEffectAI((Spell) sa, false, true);
|
||||||
}
|
}
|
||||||
if (!player.cantLoseForZeroOrLessLife() && player.canLoseLife() &&
|
if (!player.cantLoseForZeroOrLessLife() && player.canLoseLife() &&
|
||||||
|
|||||||
@@ -651,7 +651,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
// TODO sort negatives to remove from best Cards first?
|
// TODO sort negatives to remove from best Cards first?
|
||||||
for (final Card crd : negatives) {
|
for (final Card crd : negatives) {
|
||||||
for (Map.Entry<CounterType, Integer> e : table.filterToRemove(crd).entrySet()) {
|
for (Map.Entry<CounterType, Integer> e : table.filterToRemove(crd).entrySet()) {
|
||||||
if (ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
|
if (ComputerUtil.isNegativeCounter(e.getKey(), crd) && crd.canRemoveCounters(e.getKey())) {
|
||||||
int over = Math.min(e.getValue(), c - toRemove);
|
int over = Math.min(e.getValue(), c - toRemove);
|
||||||
if (over > 0) {
|
if (over > 0) {
|
||||||
toRemove += over;
|
toRemove += over;
|
||||||
@@ -762,7 +762,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if table is empty, than no counter was removed
|
// if table is empty, then no counter was removed
|
||||||
return table.isEmpty() ? null : PaymentDecision.counters(table);
|
return table.isEmpty() ? null : PaymentDecision.counters(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -836,10 +836,9 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static CardCollection chooseUntapType(final Player ai, final String type, final Card activate, final boolean untap, final int amount, SpellAbility sa) {
|
public static CardCollection chooseUntapType(final Player ai, final String type, final Card activate, final boolean untap, final int amount, SpellAbility sa) {
|
||||||
CardCollection typeList =
|
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), activate.getController(), activate, sa);
|
||||||
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), activate.getController(), activate, sa);
|
|
||||||
|
|
||||||
typeList = CardLists.filter(typeList, Presets.TAPPED);
|
typeList = CardLists.filter(typeList, Presets.TAPPED, c -> c.getCounters(CounterEnumType.STUN) == 0 || c.canRemoveCounters(CounterType.get(CounterEnumType.STUN)));
|
||||||
|
|
||||||
if (untap) {
|
if (untap) {
|
||||||
typeList.remove(activate);
|
typeList.remove(activate);
|
||||||
@@ -851,12 +850,7 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
CardLists.sortByPowerDesc(typeList);
|
CardLists.sortByPowerDesc(typeList);
|
||||||
|
|
||||||
final CardCollection untapList = new CardCollection();
|
return typeList.subList(0, amount);
|
||||||
|
|
||||||
for (int i = 0; i < amount; i++) {
|
|
||||||
untapList.add(typeList.get(i));
|
|
||||||
}
|
|
||||||
return untapList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CardCollection chooseReturnType(final Player ai, final String type, final Card activate, final Card target, final int amount, SpellAbility sa) {
|
public static CardCollection chooseReturnType(final Player ai, final String type, final Card activate, final Card target, final int amount, SpellAbility sa) {
|
||||||
|
|||||||
@@ -222,6 +222,8 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO stun counters with canRemoveCounters check
|
||||||
|
|
||||||
// remove P1P1 counters from opposing creatures
|
// remove P1P1 counters from opposing creatures
|
||||||
CardCollection oppP1P1List = CardLists.filter(list,
|
CardCollection oppP1P1List = CardLists.filter(list,
|
||||||
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
|
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-core</artifactId>
|
<artifactId>forge-core</artifactId>
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ public class DeckHints {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (String tok : card.getTokens()) {
|
for (String tok : card.getTokens()) {
|
||||||
if (tdb != null && tdb.containsRule(tok) && predicate.apply(tdb.getToken(tok).getRules())) {
|
if (tdb != null && tdb.containsRule(tok) && rulesWithTokens(predicate).apply(tdb.getToken(tok).getRules())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-game</artifactId>
|
<artifactId>forge-game</artifactId>
|
||||||
|
|||||||
@@ -309,8 +309,10 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
|
|
||||||
abstract public void setCounters(final Map<CounterType, Integer> allCounters);
|
abstract public void setCounters(final Map<CounterType, Integer> allCounters);
|
||||||
|
|
||||||
|
abstract public boolean canRemoveCounters(final CounterType type);
|
||||||
|
|
||||||
abstract public boolean canReceiveCounters(final CounterType type);
|
abstract public boolean canReceiveCounters(final CounterType type);
|
||||||
abstract public void subtractCounter(final CounterType counterName, final int n, final Player remover);
|
abstract public int subtractCounter(final CounterType counterName, final int n, final Player remover);
|
||||||
abstract public void clearCounters();
|
abstract public void clearCounters();
|
||||||
|
|
||||||
public boolean canReceiveCounters(final CounterEnumType type) {
|
public boolean canReceiveCounters(final CounterEnumType type) {
|
||||||
@@ -331,8 +333,8 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
addCounter(CounterType.get(counterType), n, source, table);
|
addCounter(CounterType.get(counterType), n, source, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void subtractCounter(final CounterEnumType counterName, final int n, final Player remover) {
|
public int subtractCounter(final CounterEnumType counterName, final int n, final Player remover) {
|
||||||
subtractCounter(CounterType.get(counterName), n, remover);
|
return subtractCounter(CounterType.get(counterName), n, remover);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map<AbilityKey, Object> params);
|
abstract public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map<AbilityKey, Object> params);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import com.google.common.base.Predicates;
|
|||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
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.CardStateName;
|
||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.game.*;
|
import forge.game.*;
|
||||||
@@ -27,7 +26,9 @@ import org.apache.commons.lang3.ObjectUtils;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class ChangeZoneEffect extends SpellAbilityEffect {
|
public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -1085,8 +1086,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
String selectPrompt = sa.hasParam("SelectPrompt") ? sa.getParam("SelectPrompt") : MessageUtil.formatMessage(Localizer.getInstance().getMessage("lblSelectCardFromPlayerZone", "{player's}", Lang.joinHomogenous(origin, ZoneType::getTranslatedName).toLowerCase()), decider, player);
|
String selectPrompt = sa.hasParam("SelectPrompt") ? sa.getParam("SelectPrompt") : MessageUtil.formatMessage(Localizer.getInstance().getMessage("lblSelectCardFromPlayerZone", "{player's}", Lang.joinHomogenous(origin, ZoneType::getTranslatedName).toLowerCase()), decider, player);
|
||||||
final String totalcmc = sa.getParam("WithTotalCMC");
|
final String totalcmc = sa.getParam("WithTotalCMC");
|
||||||
final String totalpower = sa.getParam("WithTotalPower");
|
final String totalpower = sa.getParam("WithTotalPower");
|
||||||
|
final String totalCardTypes = sa.getParam("WithTotalCardTypes");
|
||||||
int totcmc = AbilityUtils.calculateAmount(source, totalcmc, sa);
|
int totcmc = AbilityUtils.calculateAmount(source, totalcmc, sa);
|
||||||
int totpower = AbilityUtils.calculateAmount(source, totalpower, sa);
|
int totpower = AbilityUtils.calculateAmount(source, totalpower, sa);
|
||||||
|
int totCardTypes = AbilityUtils.calculateAmount(source, totalCardTypes, sa);
|
||||||
|
|
||||||
CardCollection chosenCards = new CardCollection();
|
CardCollection chosenCards = new CardCollection();
|
||||||
if (changeType.startsWith("EACH")) {
|
if (changeType.startsWith("EACH")) {
|
||||||
@@ -1160,6 +1163,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// If we're choosing multiple cards, only need to show the reveal dialog the first time through.
|
// If we're choosing multiple cards, only need to show the reveal dialog the first time through.
|
||||||
boolean shouldReveal = (i == 0);
|
boolean shouldReveal = (i == 0);
|
||||||
Card c = null;
|
Card c = null;
|
||||||
@@ -1170,6 +1174,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
c = Aggregates.random(fetchList);
|
c = Aggregates.random(fetchList);
|
||||||
} else if (defined && !chooseFromDef) {
|
} else if (defined && !chooseFromDef) {
|
||||||
c = Iterables.getFirst(fetchList, null);
|
c = Iterables.getFirst(fetchList, null);
|
||||||
|
} else if (totalCardTypes != null) {
|
||||||
|
String title = selectPrompt;
|
||||||
|
title += "\nCard types left: " + Math.max(totCardTypes, 0);
|
||||||
|
c = decider.getController().chooseSingleCardForZoneChange(destination, origin, sa, fetchList, shouldReveal ? delayedReveal : null, title, !mandatory, decider);
|
||||||
} else {
|
} else {
|
||||||
String title = selectPrompt;
|
String title = selectPrompt;
|
||||||
if (changeNum > 1) { //indicate progress if multiple cards being chosen
|
if (changeNum > 1) { //indicate progress if multiple cards being chosen
|
||||||
@@ -1202,6 +1210,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
if (totalpower != null) {
|
if (totalpower != null) {
|
||||||
totpower -= c.getCurrentPower();
|
totpower -= c.getCurrentPower();
|
||||||
}
|
}
|
||||||
|
if (totalCardTypes != null) {
|
||||||
|
totCardTypes -= Iterables.size(c.getType().getCoreTypes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalCardTypes != null && totCardTypes > 0) {
|
||||||
|
chosenCards.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1537,7 +1552,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
&& !sa.hasParam("AtRandom")
|
&& !sa.hasParam("AtRandom")
|
||||||
&& (!sa.hasParam("Defined") || sa.hasParam("ChooseFromDefined"))
|
&& (!sa.hasParam("Defined") || sa.hasParam("ChooseFromDefined"))
|
||||||
&& !sa.hasParam("WithTotalCMC")
|
&& !sa.hasParam("WithTotalCMC")
|
||||||
&& !sa.hasParam("WithTotalPower");
|
&& !sa.hasParam("WithTotalPower")
|
||||||
|
&& !sa.hasParam("WithTotalCardTypes");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -101,8 +101,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
// uses for multi sources -> one defined/target
|
// uses for multi sources -> one defined/target
|
||||||
// this needs given counter type
|
// this needs given counter type
|
||||||
if (sa.hasParam("ValidSource")) {
|
if (sa.hasParam("ValidSource")) {
|
||||||
CardCollectionView srcCards = game.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView srcCards = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("ValidSource"), activator, host, sa);
|
||||||
srcCards = CardLists.getValidCards(srcCards, sa.getParam("ValidSource"), activator, host, sa);
|
|
||||||
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
||||||
|
|
||||||
if (tgtCards.isEmpty()) {
|
if (tgtCards.isEmpty()) {
|
||||||
@@ -147,11 +146,6 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
Map<CounterType, Integer> countersToAdd = Maps.newHashMap();
|
Map<CounterType, Integer> countersToAdd = Maps.newHashMap();
|
||||||
|
|
||||||
for (Card src : srcCards) {
|
for (Card src : srcCards) {
|
||||||
// rule 121.5: If the first and second objects are the same object, nothing happens
|
|
||||||
if (src.equals(dest)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("All".equals(counterName)) {
|
if ("All".equals(counterName)) {
|
||||||
final Map<CounterType, Integer> tgtCounters = Maps.newHashMap(src.getCounters());
|
final Map<CounterType, Integer> tgtCounters = Maps.newHashMap(src.getCounters());
|
||||||
for (Map.Entry<CounterType, Integer> e : tgtCounters.entrySet()) {
|
for (Map.Entry<CounterType, Integer> e : tgtCounters.entrySet()) {
|
||||||
@@ -183,8 +177,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
params.put("CounterType", cType);
|
params.put("CounterType", cType);
|
||||||
params.put("Source", source);
|
params.put("Source", source);
|
||||||
|
|
||||||
CardCollectionView tgtCards = game.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView tgtCards = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("ValidDefined"), activator, host, sa);
|
||||||
tgtCards = CardLists.getValidCards(tgtCards, sa.getParam("ValidDefined"), activator, host, sa);
|
|
||||||
|
|
||||||
if (counterNum.equals("Any")) {
|
if (counterNum.equals("Any")) {
|
||||||
tgtCards = activator.getController().chooseCardsForEffect(
|
tgtCards = activator.getController().chooseCardsForEffect(
|
||||||
@@ -203,6 +196,9 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
if (!dest.canReceiveCounters(cType)) {
|
if (!dest.canReceiveCounters(cType)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!source.canRemoveCounters(cType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Card cur = game.getCardState(dest, null);
|
Card cur = game.getCardState(dest, null);
|
||||||
if (cur == null || !cur.equalsWithGameTimestamp(dest)) {
|
if (cur == null || !cur.equalsWithGameTimestamp(dest)) {
|
||||||
@@ -287,7 +283,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
final List<CounterType> typeChoices = Lists.newArrayList();
|
final List<CounterType> typeChoices = Lists.newArrayList();
|
||||||
// get types of counters
|
// get types of counters
|
||||||
for (CounterType ct : tgtCounters.keySet()) {
|
for (CounterType ct : tgtCounters.keySet()) {
|
||||||
if (dest.canReceiveCounters(ct)) {
|
if (dest.canReceiveCounters(ct) && source.canRemoveCounters(cType)) {
|
||||||
typeChoices.add(ct);
|
typeChoices.add(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,6 +333,9 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
if (!dest.canReceiveCounters(cType)) {
|
if (!dest.canReceiveCounters(cType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!src.canRemoveCounters(cType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int cmax = src.getCounters(cType);
|
int cmax = src.getCounters(cType);
|
||||||
if (cmax <= 0) {
|
if (cmax <= 0) {
|
||||||
|
|||||||
@@ -126,9 +126,20 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
|||||||
final int operandValue = AbilityUtils.calculateAmount(host, operand, sa);
|
final int operandValue = AbilityUtils.calculateAmount(host, operand, sa);
|
||||||
|
|
||||||
putCounter = !Expressions.compare(value, operator, operandValue);
|
putCounter = !Expressions.compare(value, operator, operandValue);
|
||||||
|
} else {
|
||||||
|
boolean canReceive = tgtCard.canReceiveCounters(ctype);
|
||||||
|
boolean canRemove = tgtCard.canRemoveCounters(ctype);
|
||||||
|
if (!canReceive && !canRemove) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (canReceive && !canRemove) {
|
||||||
|
putCounter = true;
|
||||||
|
} else if (!canReceive && canRemove) {
|
||||||
|
putCounter = false;
|
||||||
} else {
|
} else {
|
||||||
putCounter = pc.chooseBinary(sa, prompt, BinaryChoiceType.AddOrRemove, params);
|
putCounter = pc.chooseBinary(sa, prompt, BinaryChoiceType.AddOrRemove, params);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (putCounter) {
|
if (putCounter) {
|
||||||
tgtCard.addCounter(chosenType, counterAmount, pl, table);
|
tgtCard.addCounter(chosenType, counterAmount, pl, table);
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ public class CountersRemoveAllEffect extends SpellAbilityEffect {
|
|||||||
for (final Card tgtCard : cards) {
|
for (final Card tgtCard : cards) {
|
||||||
if (sa.hasParam("AllCounterTypes")) {
|
if (sa.hasParam("AllCounterTypes")) {
|
||||||
for (Map.Entry<CounterType, Integer> e : Lists.newArrayList(tgtCard.getCounters().entrySet())) {
|
for (Map.Entry<CounterType, Integer> e : Lists.newArrayList(tgtCard.getCounters().entrySet())) {
|
||||||
numberRemoved += e.getValue();
|
numberRemoved += tgtCard.subtractCounter(e.getKey(), e.getValue(), sa.getActivatingPlayer());
|
||||||
tgtCard.subtractCounter(e.getKey(), e.getValue(), sa.getActivatingPlayer());
|
|
||||||
}
|
}
|
||||||
//tgtCard.getCounters().clear();
|
//tgtCard.getCounters().clear();
|
||||||
continue;
|
continue;
|
||||||
@@ -74,7 +73,7 @@ public class CountersRemoveAllEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (counterAmount > 0) {
|
if (counterAmount > 0) {
|
||||||
tgtCard.subtractCounter(CounterType.getType(type), counterAmount, sa.getActivatingPlayer());
|
numberRemoved += tgtCard.subtractCounter(CounterType.getType(type), counterAmount, sa.getActivatingPlayer());
|
||||||
game.updateLastStateForCard(tgtCard);
|
game.updateLastStateForCard(tgtCard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,8 +110,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
// Removing energy
|
// Removing energy
|
||||||
if (type.equals("All")) {
|
if (type.equals("All")) {
|
||||||
for (Map.Entry<CounterType, Integer> e : Lists.newArrayList(tgtPlayer.getCounters().entrySet())) {
|
for (Map.Entry<CounterType, Integer> e : Lists.newArrayList(tgtPlayer.getCounters().entrySet())) {
|
||||||
tgtPlayer.subtractCounter(e.getKey(), e.getValue(), activator);
|
totalRemoved += tgtPlayer.subtractCounter(e.getKey(), e.getValue(), activator);
|
||||||
totalRemoved += e.getValue();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (num.equals("All")) {
|
if (num.equals("All")) {
|
||||||
@@ -120,8 +119,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
if (type.equals("Any")) {
|
if (type.equals("Any")) {
|
||||||
totalRemoved += removeAnyType(tgtPlayer, cntToRemove, sa);
|
totalRemoved += removeAnyType(tgtPlayer, cntToRemove, sa);
|
||||||
} else {
|
} else {
|
||||||
tgtPlayer.subtractCounter(counterType, cntToRemove, activator);
|
totalRemoved += tgtPlayer.subtractCounter(counterType, cntToRemove, activator);
|
||||||
totalRemoved += cntToRemove;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,11 +163,11 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
if (gameCard == null || !tgtCard.equalsWithGameTimestamp(gameCard)) {
|
if (gameCard == null || !tgtCard.equalsWithGameTimestamp(gameCard)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Zone zone = game.getZoneOf(gameCard);
|
final Zone zone = game.getZoneOf(gameCard);
|
||||||
if (type.equals("All")) {
|
if (type.equals("All")) {
|
||||||
for (Map.Entry<CounterType, Integer> e : Lists.newArrayList(gameCard.getCounters().entrySet())) {
|
for (Map.Entry<CounterType, Integer> e : Lists.newArrayList(gameCard.getCounters().entrySet())) {
|
||||||
gameCard.subtractCounter(e.getKey(), e.getValue(), activator);
|
totalRemoved += gameCard.subtractCounter(e.getKey(), e.getValue(), activator);
|
||||||
totalRemoved += e.getValue();
|
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(gameCard);
|
game.updateLastStateForCard(gameCard);
|
||||||
continue;
|
continue;
|
||||||
@@ -180,6 +178,9 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
if (type.equals("Any")) {
|
if (type.equals("Any")) {
|
||||||
totalRemoved += removeAnyType(gameCard, cntToRemove, sa);
|
totalRemoved += removeAnyType(gameCard, cntToRemove, sa);
|
||||||
} else {
|
} else {
|
||||||
|
if (!tgtCard.canRemoveCounters(counterType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
cntToRemove = Math.min(cntToRemove, gameCard.getCounters(counterType));
|
cntToRemove = Math.min(cntToRemove, gameCard.getCounters(counterType));
|
||||||
|
|
||||||
if (zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Exile)) {
|
if (zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Exile)) {
|
||||||
@@ -221,14 +222,18 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
final Player activator = sa.getActivatingPlayer();
|
final Player activator = sa.getActivatingPlayer();
|
||||||
final PlayerController pc = activator.getController();
|
final PlayerController pc = activator.getController();
|
||||||
final Map<CounterType, Integer> tgtCounters = Maps.newHashMap(entity.getCounters());
|
final Map<CounterType, Integer> tgtCounters = Maps.newHashMap(entity.getCounters());
|
||||||
|
for (CounterType ct : ImmutableList.copyOf(tgtCounters.keySet())) {
|
||||||
|
if (!entity.canRemoveCounters(ct)) {
|
||||||
|
tgtCounters.remove(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (cntToRemove > 0 && !tgtCounters.isEmpty()) {
|
while (cntToRemove > 0 && !tgtCounters.isEmpty()) {
|
||||||
Map<String, Object> params = Maps.newHashMap();
|
Map<String, Object> params = Maps.newHashMap();
|
||||||
params.put("Target", entity);
|
params.put("Target", entity);
|
||||||
|
|
||||||
String prompt = Localizer.getInstance().getMessage("lblSelectCountersTypeToRemove");
|
String prompt = Localizer.getInstance().getMessage("lblSelectCountersTypeToRemove");
|
||||||
CounterType chosenType = pc.chooseCounterType(
|
CounterType chosenType = pc.chooseCounterType(ImmutableList.copyOf(tgtCounters.keySet()), sa, prompt, params);
|
||||||
ImmutableList.copyOf(tgtCounters.keySet()), sa, prompt, params);
|
|
||||||
|
|
||||||
int max = Math.min(cntToRemove, tgtCounters.get(chosenType));
|
int max = Math.min(cntToRemove, tgtCounters.get(chosenType));
|
||||||
// remove selection so player can't cheat additional trigger by choosing the same type multiple times
|
// remove selection so player can't cheat additional trigger by choosing the same type multiple times
|
||||||
|
|||||||
@@ -1600,6 +1600,21 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean canRemoveCounters(final CounterType type) {
|
||||||
|
if (isPhasedOut()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
|
||||||
|
repParams.put(AbilityKey.CounterType, type);
|
||||||
|
repParams.put(AbilityKey.Result, 0);
|
||||||
|
repParams.put(AbilityKey.IsDamage, false);
|
||||||
|
if (game.getReplacementHandler().cantHappenCheck(ReplacementType.RemoveCounter, repParams)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map<AbilityKey, Object> params) {
|
public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map<AbilityKey, Object> params) {
|
||||||
int addAmount = n;
|
int addAmount = n;
|
||||||
@@ -1736,11 +1751,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void subtractCounter(final CounterType counterName, final int n, final Player remover) {
|
public final int subtractCounter(final CounterType counterName, final int n, final Player remover) {
|
||||||
subtractCounter(counterName, n, remover, false);
|
return subtractCounter(counterName, n, remover, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void subtractCounter(final CounterType counterName, final int n, final Player remover, final boolean isDamage) {
|
public final int subtractCounter(final CounterType counterName, final int n, final Player remover, final boolean isDamage) {
|
||||||
int oldValue = getCounters(counterName);
|
int oldValue = getCounters(counterName);
|
||||||
int newValue = Math.max(oldValue - n, 0);
|
int newValue = Math.max(oldValue - n, 0);
|
||||||
|
|
||||||
@@ -1758,12 +1773,14 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
|||||||
newValue = 0;
|
newValue = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Replaced:
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int delta = oldValue - newValue;
|
final int delta = oldValue - newValue;
|
||||||
if (delta == 0) { return; }
|
if (delta == 0) { return 0; }
|
||||||
|
|
||||||
int powerBonusBefore = getPowerBonusFromCounters();
|
int powerBonusBefore = getPowerBonusFromCounters();
|
||||||
int toughnessBonusBefore = getToughnessBonusFromCounters();
|
int toughnessBonusBefore = getToughnessBonusFromCounters();
|
||||||
@@ -1800,6 +1817,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars, ITr
|
|||||||
runParams.put(AbilityKey.CounterAmount, delta);
|
runParams.put(AbilityKey.CounterAmount, delta);
|
||||||
runParams.put(AbilityKey.NewCounterAmount, newValue);
|
runParams.put(AbilityKey.NewCounterAmount, newValue);
|
||||||
getGame().getTriggerHandler().runTrigger(TriggerType.CounterRemovedOnce, runParams, false);
|
getGame().getTriggerHandler().runTrigger(TriggerType.CounterRemovedOnce, runParams, false);
|
||||||
|
|
||||||
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -70,10 +70,16 @@ public class CostRemoveAnyCounter extends CostPart {
|
|||||||
int allCounters = 0;
|
int allCounters = 0;
|
||||||
for (Card c : validCards) {
|
for (Card c : validCards) {
|
||||||
if (this.counter != null) {
|
if (this.counter != null) {
|
||||||
|
if (!c.canRemoveCounters(this.counter)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
allCounters += c.getCounters(this.counter);
|
allCounters += c.getCounters(this.counter);
|
||||||
} else {
|
} else {
|
||||||
for (Integer value : c.getCounters().values()) {
|
for (Map.Entry<CounterType, Integer> entry : c.getCounters().entrySet()) {
|
||||||
allCounters += value;
|
if (!c.canRemoveCounters(entry.getKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
allCounters += entry.getValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import com.google.common.collect.Maps;
|
|||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
|
import forge.game.card.CounterEnumType;
|
||||||
|
import forge.game.card.CounterType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
@@ -78,7 +80,8 @@ public class CostUntap extends CostPart {
|
|||||||
@Override
|
@Override
|
||||||
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) {
|
||||||
final Card source = ability.getHostCard();
|
final Card source = ability.getHostCard();
|
||||||
return source.isTapped() && !source.isAbilitySick();
|
return source.isTapped() && !source.isAbilitySick() &&
|
||||||
|
(source.getCounters(CounterEnumType.STUN) == 0 || source.canRemoveCounters(CounterType.get(CounterEnumType.STUN)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ 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.CounterEnumType;
|
||||||
|
import forge.game.card.CounterType;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -90,7 +92,7 @@ public class CostUntapType extends CostPartWithList {
|
|||||||
if (!canUntapSource) {
|
if (!canUntapSource) {
|
||||||
typeList.remove(source);
|
typeList.remove(source);
|
||||||
}
|
}
|
||||||
typeList = CardLists.filter(typeList, Presets.TAPPED);
|
typeList = CardLists.filter(typeList, Presets.TAPPED, c -> c.getCounters(CounterEnumType.STUN) == 0 || c.canRemoveCounters(CounterType.get(CounterEnumType.STUN)));
|
||||||
|
|
||||||
final int amount = this.getAbilityAmount(ability);
|
final int amount = this.getAbilityAmount(ability);
|
||||||
return (typeList.size() != 0) && (typeList.size() >= amount);
|
return (typeList.size() != 0) && (typeList.size() >= amount);
|
||||||
|
|||||||
@@ -1049,6 +1049,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
for (SpellAbility sa : chosenSa) {
|
for (SpellAbility sa : chosenSa) {
|
||||||
Card saHost = sa.getHostCard();
|
Card saHost = sa.getHostCard();
|
||||||
final Zone originZone = saHost.getZone();
|
final Zone originZone = saHost.getZone();
|
||||||
|
final CardZoneTable triggerList = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
||||||
|
|
||||||
if (pPlayerPriority.getController().playChosenSpellAbility(sa)) {
|
if (pPlayerPriority.getController().playChosenSpellAbility(sa)) {
|
||||||
// 117.3c If a player has priority when they cast a spell, activate an ability, [play a land]
|
// 117.3c If a player has priority when they cast a spell, activate an ability, [play a land]
|
||||||
@@ -1064,7 +1065,6 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
// Need to check if Zone did change
|
// Need to check if Zone did change
|
||||||
if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa.isLandAbility())) {
|
if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa.isLandAbility())) {
|
||||||
// currently there can be only one Spell put on the Stack at once, or Land Abilities be played
|
// currently there can be only one Spell put on the Stack at once, or Land Abilities be played
|
||||||
final CardZoneTable triggerList = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
|
||||||
triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost);
|
triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost);
|
||||||
triggerList.triggerChangesZoneAll(game, sa);
|
triggerList.triggerChangesZoneAll(game, sa);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -855,6 +855,14 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean canRemoveCounters(final CounterType type) {
|
||||||
|
if (!isInGame()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// no RE affecting players currently, skip check for performance
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map<AbilityKey, Object> params) {
|
public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map<AbilityKey, Object> params) {
|
||||||
int addAmount = n;
|
int addAmount = n;
|
||||||
@@ -896,12 +904,12 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void subtractCounter(CounterType counterName, int num, final Player remover) {
|
public int subtractCounter(CounterType counterName, int num, final Player remover) {
|
||||||
int oldValue = getCounters(counterName);
|
int oldValue = getCounters(counterName);
|
||||||
int newValue = Math.max(oldValue - num, 0);
|
int newValue = Math.max(oldValue - num, 0);
|
||||||
|
|
||||||
final int delta = oldValue - newValue;
|
final int delta = oldValue - newValue;
|
||||||
if (delta == 0) { return; }
|
if (delta == 0) { return 0; }
|
||||||
|
|
||||||
setCounters(counterName, newValue, null, true);
|
setCounters(counterName, newValue, null, true);
|
||||||
|
|
||||||
@@ -917,6 +925,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
getGame().getTriggerHandler().runTrigger(TriggerType.CounterRemoved, runParams, false);
|
getGame().getTriggerHandler().runTrigger(TriggerType.CounterRemoved, runParams, false);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void clearCounters() {
|
public final void clearCounters() {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="forge.app"
|
package="forge.app"
|
||||||
android:versionCode="106650000"
|
android:versionCode="106660000"
|
||||||
android:versionName="1.6.65" > <!-- versionName should be updated and it's used for Sentry releases tag -->
|
android:versionName="1.6.66" > <!-- versionName should be updated and it's used for Sentry releases tag -->
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="26"
|
android:minSdkVersion="26"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<packaging.type>jar</packaging.type>
|
<packaging.type>jar</packaging.type>
|
||||||
<build.min.memory>-Xms1024m</build.min.memory>
|
<build.min.memory>-Xms1024m</build.min.memory>
|
||||||
<build.max.memory>-Xmx1536m</build.max.memory>
|
<build.max.memory>-Xmx1536m</build.max.memory>
|
||||||
<alpha-version>1.6.65-SNAPSHOT</alpha-version>
|
<alpha-version>1.6.66-SNAPSHOT</alpha-version>
|
||||||
<sign.keystore>keystore</sign.keystore>
|
<sign.keystore>keystore</sign.keystore>
|
||||||
<sign.alias>alias</sign.alias>
|
<sign.alias>alias</sign.alias>
|
||||||
<sign.storepass>storepass</sign.storepass>
|
<sign.storepass>storepass</sign.storepass>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-android</artifactId>
|
<artifactId>forge-gui-android</artifactId>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-desktop</artifactId>
|
<artifactId>forge-gui-desktop</artifactId>
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<!-- TODO: insert placeholder for latest version tag -->
|
<!-- TODO: insert placeholder for latest version tag -->
|
||||||
<fromRef>forge-1.6.64</fromRef>
|
<fromRef>forge-1.6.65</fromRef>
|
||||||
<file>../forge-gui/release-files/CHANGES.txt</file>
|
<file>../forge-gui/release-files/CHANGES.txt</file>
|
||||||
<templateContent>
|
<templateContent>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-ios</artifactId>
|
<artifactId>forge-gui-ios</artifactId>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-mobile-dev</artifactId>
|
<artifactId>forge-gui-mobile-dev</artifactId>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-mobile</artifactId>
|
<artifactId>forge-gui-mobile</artifactId>
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
package forge;
|
package forge;
|
||||||
|
|
||||||
import com.badlogic.gdx.Application;
|
import com.badlogic.gdx.*;
|
||||||
import com.badlogic.gdx.ApplicationListener;
|
|
||||||
import com.badlogic.gdx.Gdx;
|
|
||||||
import com.badlogic.gdx.Input.Keys;
|
import com.badlogic.gdx.Input.Keys;
|
||||||
import com.badlogic.gdx.Input;
|
|
||||||
import com.badlogic.gdx.InputProcessor;
|
|
||||||
import com.badlogic.gdx.controllers.Controller;
|
import com.badlogic.gdx.controllers.Controller;
|
||||||
import com.badlogic.gdx.controllers.ControllerAdapter;
|
import com.badlogic.gdx.controllers.ControllerAdapter;
|
||||||
import com.badlogic.gdx.controllers.ControllerListener;
|
import com.badlogic.gdx.controllers.ControllerListener;
|
||||||
@@ -54,7 +50,7 @@ import java.nio.file.Paths;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class Forge implements ApplicationListener {
|
public class Forge implements ApplicationListener {
|
||||||
public static final String CURRENT_VERSION = "1.6.65-SNAPSHOT";
|
public static final String CURRENT_VERSION = "1.6.66-SNAPSHOT";
|
||||||
|
|
||||||
private static ApplicationListener app = null;
|
private static ApplicationListener app = null;
|
||||||
static Scene currentScene = null;
|
static Scene currentScene = null;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.65-SNAPSHOT</version>
|
<version>1.6.66-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui</artifactId>
|
<artifactId>forge-gui</artifactId>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:4 R
|
|||||||
Types:Creature Minotaur Shaman
|
Types:Creature Minotaur Shaman
|
||||||
PT:3/4
|
PT:3/4
|
||||||
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ ABImpulse | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, you may put an instant or sorcery card from your graveyard on the bottom of your library. If you do, exile the top two cards of your library. You may play those cards this turn.
|
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ ABImpulse | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, you may put an instant or sorcery card from your graveyard on the bottom of your library. If you do, exile the top two cards of your library. You may play those cards this turn.
|
||||||
SVar:ABImpulse:AB$ Dig | Cost$ PutCardToLibFromGrave<1/-1/Sorcery;Instant> | Defined$ You | DigNum$ 2 | ChangeNum$ All | DestinationZone$ Exile | SubAbility$ DBEffect | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Exile the top two cards of your library. You may play those cards this turn.
|
SVar:ABImpulse:AB$ Dig | Cost$ PutCardToLibFromGrave<1/-1/Sorcery;Instant> | Defined$ You | DigNum$ 2 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Exile the top two cards of your library. You may play those cards this turn.
|
||||||
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | SubAbility$ DBCleanup | ForgetOnMoved$ Exile
|
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | SubAbility$ DBCleanup | ForgetOnMoved$ Exile
|
||||||
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play the exiled cards this turn.
|
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play the exiled cards this turn.
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ Types:Legendary Creature Human Berserker
|
|||||||
PT:3/3
|
PT:3/3
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.YouCtrl+attacking | TriggerZones$ Battlefield | Execute$ TrigChangeZoneBis | TriggerDescription$ Whenever a creature you control attacks or a creature you control enters attacking, you may pay {1}{R}. If you do, you may put a creature card with mana value less than that creature's mana value from your hand onto the battlefield tapped and attacking.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.YouCtrl+attacking | TriggerZones$ Battlefield | Execute$ TrigChangeZoneBis | TriggerDescription$ Whenever a creature you control attacks or a creature you control enters attacking, you may pay {1}{R}. If you do, you may put a creature card with mana value less than that creature's mana value from your hand onto the battlefield tapped and attacking.
|
||||||
T:Mode$ Attacks | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigChangeZone | Secondary$ True | TriggerDescription$ Whenever a creature you control attacks or a creature you control enters attacking, you may pay {1}{R}. If you do, you may put a creature card with mana value less than that creature's mana value from your hand onto the battlefield tapped and attacking.
|
T:Mode$ Attacks | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigChangeZone | Secondary$ True | TriggerDescription$ Whenever a creature you control attacks or a creature you control enters attacking, you may pay {1}{R}. If you do, you may put a creature card with mana value less than that creature's mana value from your hand onto the battlefield tapped and attacking.
|
||||||
SVar:TrigChangeZone:AB$ ChangeZone | Cost$ 1 R | Origin$ Hand | ChangeNum$ 1 | Destination$ Battlefield | ChangeType$ Creature.cmcLTX+YouCtrl | ChangeNum$ 1 | Tapped$ True | Attacking$ True
|
SVar:TrigChangeZone:AB$ ChangeZone | Cost$ 1 R | Origin$ Hand | Destination$ Battlefield | ChangeType$ Creature.cmcLTX+YouCtrl | ChangeNum$ 1 | Tapped$ True | Attacking$ True
|
||||||
SVar:TrigChangeZoneBis:AB$ ChangeZone | Cost$ 1 R | Origin$ Hand | ChangeNum$ 1 | Destination$ Battlefield | ChangeType$ Creature.cmcLTY+YouCtrl | ChangeNum$ 1 | Tapped$ True | Attacking$ True
|
SVar:TrigChangeZoneBis:AB$ ChangeZone | Cost$ 1 R | Origin$ Hand | Destination$ Battlefield | ChangeType$ Creature.cmcLTY+YouCtrl | ChangeNum$ 1 | Tapped$ True | Attacking$ True
|
||||||
SVar:X:TriggeredAttacker$CardManaCost
|
SVar:X:TriggeredAttacker$CardManaCost
|
||||||
SVar:Y:TriggeredCard$CardManaCost
|
SVar:Y:TriggeredCard$CardManaCost
|
||||||
Oracle:Whenever a creature you control attacks or a creature you control enters attacking, you may pay {1}{R}. If you do, you may put a creature card with mana value less than that creature's mana value from your hand onto the battlefield tapped and attacking.
|
Oracle:Whenever a creature you control attacks or a creature you control enters attacking, you may pay {1}{R}. If you do, you may put a creature card with mana value less than that creature's mana value from your hand onto the battlefield tapped and attacking.
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ ManaCost:3 R
|
|||||||
Types:Creature Wurm
|
Types:Creature Wurm
|
||||||
PT:4/2
|
PT:4/2
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Landfall — Whenever a land you control enters, CARDNAME gains first strike until end of turn.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Landfall — Whenever a land you control enters, CARDNAME gains first strike until end of turn.
|
||||||
SVar:TrigPump:DB$ Pump | Defined$ Self | KW$ First Strike | Defined$ Self
|
SVar:TrigPump:DB$ Pump | Defined$ Self | KW$ First Strike
|
||||||
SVar:BuffedBy:Land
|
SVar:BuffedBy:Land
|
||||||
Oracle:Landfall — Whenever a land you control enters, Belligerent Whiptail gains first strike until end of turn.
|
Oracle:Landfall — Whenever a land you control enters, Belligerent Whiptail gains first strike until end of turn.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:Blight Herder
|
|||||||
ManaCost:5
|
ManaCost:5
|
||||||
Types:Creature Eldrazi Processor
|
Types:Creature Eldrazi Processor
|
||||||
PT:4/5
|
PT:4/5
|
||||||
T:Mode$ SpellCast | ValidCard$ Card.Self | OptionalDecider$ You | Execute$ TrigToken | OptionalDecider$ You | TriggerDescription$ When you cast this spell, you may put two cards your opponents own from exile into their owners' graveyards. If you do, create three 1/1 colorless Eldrazi Scion creature tokens. They have "Sacrifice this creature: Add {C}."
|
T:Mode$ SpellCast | ValidCard$ Card.Self | Execute$ TrigToken | OptionalDecider$ You | TriggerDescription$ When you cast this spell, you may put two cards your opponents own from exile into their owners' graveyards. If you do, create three 1/1 colorless Eldrazi Scion creature tokens. They have "Sacrifice this creature: Add {C}."
|
||||||
SVar:TrigToken:AB$ Token | Cost$ ExiledMoveToGrave<2/Card.OppOwn/cards your opponents own> | TokenAmount$ 3 | TokenScript$ c_1_1_eldrazi_scion_sac | TokenOwner$ You
|
SVar:TrigToken:AB$ Token | Cost$ ExiledMoveToGrave<2/Card.OppOwn/cards your opponents own> | TokenAmount$ 3 | TokenScript$ c_1_1_eldrazi_scion_sac | TokenOwner$ You
|
||||||
DeckHints:Keyword$Ingest & Type$Eldrazi
|
DeckHints:Keyword$Ingest & Type$Eldrazi
|
||||||
DeckHas:Ability$Mana.Colorless|Token
|
DeckHas:Ability$Mana.Colorless|Token
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Creature Squirrel Druid
|
|||||||
PT:3/3
|
PT:3/3
|
||||||
K:Toxic:2
|
K:Toxic:2
|
||||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ WWen CARDNAME enters, you and target opponent each create a Treasure token.
|
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ WWen CARDNAME enters, you and target opponent each create a Treasure token.
|
||||||
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | ValidTgts$ Opponent | TokenOwner$ TargetedAndYou | TokenAmount$ 1 | TokenScript$ c_a_treasure_sac | SpellDescription$ You and target opponent each create a Food token.
|
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | ValidTgts$ Opponent | TokenOwner$ TargetedAndYou | TokenScript$ c_a_treasure_sac | SpellDescription$ You and target opponent each create a Food token.
|
||||||
T:Mode$ Sacrificed | ValidCard$ Permanent.token+nonCreature | ValidPlayer$ Opponent | Execute$ TrigPoison | TriggerZones$ Battlefield | TriggerDescription$ Whenever an opponent sacrifices a noncreature token, that player gets two poison counters.
|
T:Mode$ Sacrificed | ValidCard$ Permanent.token+nonCreature | ValidPlayer$ Opponent | Execute$ TrigPoison | TriggerZones$ Battlefield | TriggerDescription$ Whenever an opponent sacrifices a noncreature token, that player gets two poison counters.
|
||||||
SVar:TrigPoison:DB$ Poison | Defined$ TriggeredCardController | Num$ 2
|
SVar:TrigPoison:DB$ Poison | Defined$ TriggeredCardController | Num$ 2
|
||||||
Oracle:Toxic 2 (Players dealt combat damage by this creature also get two poison counters. A player with ten or more poison counters loses the game.)\nWhen Bloodroot Apothecary enters, you and target opponent each create a Treasure token.\nWhenever an opponent sacrifices a noncreature token, that player gets two poison counters.
|
Oracle:Toxic 2 (Players dealt combat damage by this creature also get two poison counters. A player with ten or more poison counters loses the game.)\nWhen Bloodroot Apothecary enters, you and target opponent each create a Treasure token.\nWhenever an opponent sacrifices a noncreature token, that player gets two poison counters.
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ ManaCost:3 R
|
|||||||
Types:Creature Minotaur Warrior
|
Types:Creature Minotaur Warrior
|
||||||
PT:4/3
|
PT:4/3
|
||||||
T:Mode$ Attacks | ValidCard$ Card.Self | IsPresent$ Warrior.Other+YourTeamCtrl | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks, if your team controls another Warrior, CARDNAME gains first strike until end of turn.
|
T:Mode$ Attacks | ValidCard$ Card.Self | IsPresent$ Warrior.Other+YourTeamCtrl | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks, if your team controls another Warrior, CARDNAME gains first strike until end of turn.
|
||||||
SVar:TrigPump:DB$ Pump | Defined$ Self | KW$ First Strike | Defined$ Self
|
SVar:TrigPump:DB$ Pump | Defined$ Self | KW$ First Strike
|
||||||
SVar:BuffedBy:Warrior
|
SVar:BuffedBy:Warrior
|
||||||
Oracle:Whenever Bull-Rush Bruiser attacks, if your team controls another Warrior, Bull-Rush Bruiser gains first strike until end of turn.
|
Oracle:Whenever Bull-Rush Bruiser attacks, if your team controls another Warrior, Bull-Rush Bruiser gains first strike until end of turn.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:1 B
|
|||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You.descended | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of your end step, if you descended this turn, put a +1/+1 counter on target creature you control. (You descended if a permanent card was put into your graveyard from anywhere.)
|
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You.descended | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of your end step, if you descended this turn, put a +1/+1 counter on target creature you control. (You descended if a permanent card was put into your graveyard from anywhere.)
|
||||||
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterNum$ 1 | CounterType$ P1P1
|
SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterNum$ 1 | CounterType$ P1P1
|
||||||
A:AB$ Token | Cost$ 5 B B Sac<1/CARDNAME> | TokenOwner$ You | TokenScript$ wb_4_3_vampire_demon_flying | TokenOwner$ You | SpellDescription$ Create a 4/3 white and black Vampire Demon creature token with flying.
|
A:AB$ Token | Cost$ 5 B B Sac<1/CARDNAME> | TokenScript$ wb_4_3_vampire_demon_flying | TokenOwner$ You | SpellDescription$ Create a 4/3 white and black Vampire Demon creature token with flying.
|
||||||
DeckHas:Ability$Sacrifice|Token|Counters & Type$Vampire|Demon
|
DeckHas:Ability$Sacrifice|Token|Counters & Type$Vampire|Demon
|
||||||
DeckHints:Ability$Mill|Sacrifice
|
DeckHints:Ability$Mill|Sacrifice
|
||||||
Oracle:At the beginning of your end step, if you descended this turn, put a +1/+1 counter on target creature you control. (You descended if a permanent card was put into your graveyard from anywhere.)\n{5}{B}{B}, Sacrifice Canonized in Blood: Create a 4/3 white and black Vampire Demon creature token with flying.
|
Oracle:At the beginning of your end step, if you descended this turn, put a +1/+1 counter on target creature you control. (You descended if a permanent card was put into your graveyard from anywhere.)\n{5}{B}{B}, Sacrifice Canonized in Blood: Create a 4/3 white and black Vampire Demon creature token with flying.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 1
|
|||||||
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | Execute$ TrigCreateAndMill | TriggerDescription$ At the beginning of your end step, create a 5/5 black Demon creature token with flying, then mill two cards. If two cards that share all their card types were milled this way, sacrifice CARDNAME.
|
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | Execute$ TrigCreateAndMill | TriggerDescription$ At the beginning of your end step, create a 5/5 black Demon creature token with flying, then mill two cards. If two cards that share all their card types were milled this way, sacrifice CARDNAME.
|
||||||
SVar:TrigCreateAndMill:DB$ Token | TokenAmount$ 1 | TokenScript$ b_5_5_demon_flying | TokenOwner$ You | SubAbility$ DBMill
|
SVar:TrigCreateAndMill:DB$ Token | TokenAmount$ 1 | TokenScript$ b_5_5_demon_flying | TokenOwner$ You | SubAbility$ DBMill
|
||||||
SVar:DBMill:DB$ Mill | NumCards$ 2 | RememberMilled$ True | ShowMilledCards$ True | SubAbility$ DBSacrifice
|
SVar:DBMill:DB$ Mill | NumCards$ 2 | RememberMilled$ True | ShowMilledCards$ True | SubAbility$ DBSacrifice
|
||||||
SVar:DBSacrifice:DB$ Sacrifice | SacValid$ Self | ShowSacrificedCards$ True | ConditionCheckSVar$ MilledSharesAllTypes | ConditionSVarCompare$ GE2 | SubAbility$ Cleanup
|
SVar:DBSacrifice:DB$ Sacrifice | SacValid$ Self | ShowSacrificedCards$ True | ConditionCheckSVar$ MilledSharesAllTypes | ConditionSVarCompare$ GE2 | SubAbility$ DBCleanup
|
||||||
SVar:MilledSharesAllTypes:Remembered$Valid Card.sharesAllCardTypesWithOther Remembered
|
SVar:MilledSharesAllTypes:Remembered$Valid Card.sharesAllCardTypesWithOther Remembered
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
DeckHas:Ability$Token|Mill|Sacrifice
|
DeckHas:Ability$Token|Mill|Sacrifice
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user