mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Merge remote-tracking branch 'upstream/master' into collector-number-in-card-list-and-card-db-refactoring
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
|
||||||
# Ignore NetBeans config files
|
# Ignore NetBeans config files
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.40-SNAPSHOT</version>
|
<version>1.6.43-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-ai</artifactId>
|
<artifactId>forge-ai</artifactId>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
@@ -43,6 +44,7 @@ import forge.game.combat.GlobalAttackRestrictions;
|
|||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
@@ -391,7 +393,7 @@ public class AiAttackController {
|
|||||||
|
|
||||||
//Calculate the amount of creatures necessary
|
//Calculate the amount of creatures necessary
|
||||||
for (int i = 0; i < list.size(); i++) {
|
for (int i = 0; i < list.size(); i++) {
|
||||||
if (!this.doesHumanAttackAndWin(ai, i)) {
|
if (!doesHumanAttackAndWin(ai, i)) {
|
||||||
blockersNeeded = i;
|
blockersNeeded = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -635,7 +637,7 @@ public class AiAttackController {
|
|||||||
if (defs.size() == 1) {
|
if (defs.size() == 1) {
|
||||||
return defs.getFirst();
|
return defs.getFirst();
|
||||||
}
|
}
|
||||||
Player prefDefender = (Player) (defs.contains(this.defendingOpponent) ? this.defendingOpponent : defs.get(0));
|
GameEntity prefDefender = defs.contains(this.defendingOpponent) ? this.defendingOpponent : defs.get(0);
|
||||||
|
|
||||||
// Attempt to see if there's a defined entity that must be attacked strictly this turn...
|
// Attempt to see if there's a defined entity that must be attacked strictly this turn...
|
||||||
GameEntity entity = ai.getMustAttackEntityThisTurn();
|
GameEntity entity = ai.getMustAttackEntityThisTurn();
|
||||||
@@ -659,7 +661,8 @@ public class AiAttackController {
|
|||||||
// 2. attack planeswalkers
|
// 2. attack planeswalkers
|
||||||
List<Card> pwDefending = c.getDefendingPlaneswalkers();
|
List<Card> pwDefending = c.getDefendingPlaneswalkers();
|
||||||
if (!pwDefending.isEmpty()) {
|
if (!pwDefending.isEmpty()) {
|
||||||
return pwDefending.get(0);
|
final Card pwNearUlti = ComputerUtilCard.getBestPlaneswalkerToDamage(pwDefending);
|
||||||
|
return pwNearUlti != null ? pwNearUlti : ComputerUtilCard.getBestPlaneswalkerAI(pwDefending);
|
||||||
} else {
|
} else {
|
||||||
return prefDefender;
|
return prefDefender;
|
||||||
}
|
}
|
||||||
@@ -695,14 +698,14 @@ public class AiAttackController {
|
|||||||
tradeIfLowerLifePressure = aic.getBooleanProperty(AiProps.RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE);
|
tradeIfLowerLifePressure = aic.getBooleanProperty(AiProps.RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean bAssault = this.doAssault(ai);
|
final boolean bAssault = doAssault(ai);
|
||||||
// TODO: detect Lightmine Field by presence of a card with a specific trigger
|
// TODO: detect Lightmine Field by presence of a card with a specific trigger
|
||||||
final boolean lightmineField = ComputerUtilCard.isPresentOnBattlefield(ai.getGame(), "Lightmine Field");
|
final boolean lightmineField = ComputerUtilCard.isPresentOnBattlefield(ai.getGame(), "Lightmine Field");
|
||||||
// TODO: detect Season of the Witch by presence of a card with a specific trigger
|
// TODO: detect Season of the Witch by presence of a card with a specific trigger
|
||||||
final boolean seasonOfTheWitch = ComputerUtilCard.isPresentOnBattlefield(ai.getGame(), "Season of the Witch");
|
final boolean seasonOfTheWitch = ComputerUtilCard.isPresentOnBattlefield(ai.getGame(), "Season of the Witch");
|
||||||
|
|
||||||
// Determine who will be attacked
|
// Determine who will be attacked
|
||||||
GameEntity defender = this.chooseDefender(combat, bAssault);
|
GameEntity defender = chooseDefender(combat, bAssault);
|
||||||
List<Card> attackersLeft = new ArrayList<>(this.attackers);
|
List<Card> attackersLeft = new ArrayList<>(this.attackers);
|
||||||
|
|
||||||
// TODO probably use AttackConstraints instead of only GlobalAttackRestrictions?
|
// TODO probably use AttackConstraints instead of only GlobalAttackRestrictions?
|
||||||
@@ -1090,17 +1093,18 @@ public class AiAttackController {
|
|||||||
// if enough damage: switch to next planeswalker or player
|
// if enough damage: switch to next planeswalker or player
|
||||||
if (damage >= pw.getCounters(CounterEnumType.LOYALTY)) {
|
if (damage >= pw.getCounters(CounterEnumType.LOYALTY)) {
|
||||||
List<Card> pwDefending = combat.getDefendingPlaneswalkers();
|
List<Card> pwDefending = combat.getDefendingPlaneswalkers();
|
||||||
boolean found = false;
|
|
||||||
// look for next planeswalker
|
// look for next planeswalker
|
||||||
for (Card walker : pwDefending) {
|
for (Card walker : Lists.newArrayList(pwDefending)) {
|
||||||
if (combat.getAttackersOf(walker).isEmpty()) {
|
if (!combat.getAttackersOf(walker).isEmpty()) {
|
||||||
defender = walker;
|
pwDefending.remove(walker);
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (pwDefending.isEmpty()) {
|
||||||
defender = combat.getDefendingPlayers().get(0);
|
defender = Collections.min(Lists.newArrayList(combat.getDefendingPlayers()), PlayerPredicates.compareByLife());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final Card pwNearUlti = ComputerUtilCard.getBestPlaneswalkerToDamage(pwDefending);
|
||||||
|
defender = pwNearUlti != null ? pwNearUlti : ComputerUtilCard.getBestPlaneswalkerAI(pwDefending);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1354,8 +1358,7 @@ public class AiAttackController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if card has a Exert Trigger which would target,
|
// if card has a Exert Trigger which would target,
|
||||||
// but there are no creatures it can target, no need to exert with
|
// but there are no creatures it can target, no need to exert with it
|
||||||
// it
|
|
||||||
boolean missTarget = false;
|
boolean missTarget = false;
|
||||||
for (Trigger t : c.getTriggers()) {
|
for (Trigger t : c.getTriggers()) {
|
||||||
if (!TriggerType.Exerted.equals(t.getMode())) {
|
if (!TriggerType.Exerted.equals(t.getMode())) {
|
||||||
|
|||||||
@@ -816,7 +816,7 @@ public class AiController {
|
|||||||
if(payCosts != null) {
|
if(payCosts != null) {
|
||||||
ManaCost mana = payCosts.getTotalMana();
|
ManaCost mana = payCosts.getTotalMana();
|
||||||
if (mana != null) {
|
if (mana != null) {
|
||||||
if(mana.countX() > 0) {
|
if (mana.countX() > 0) {
|
||||||
// Set PayX here to maximum value.
|
// Set PayX here to maximum value.
|
||||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, player);
|
final int xPay = ComputerUtilCost.getMaxXValue(sa, player);
|
||||||
if (xPay <= 0) {
|
if (xPay <= 0) {
|
||||||
@@ -1819,7 +1819,7 @@ public class AiController {
|
|||||||
// AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns)
|
// AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns)
|
||||||
|
|
||||||
List<SpellAbility> result = Lists.newArrayList();
|
List<SpellAbility> result = Lists.newArrayList();
|
||||||
for(SpellAbility sa : usableFromOpeningHand) {
|
for (SpellAbility sa : usableFromOpeningHand) {
|
||||||
// Is there a better way for the AI to decide this?
|
// Is there a better way for the AI to decide this?
|
||||||
if (doTrigger(sa, false)) {
|
if (doTrigger(sa, false)) {
|
||||||
result.add(sa);
|
result.add(sa);
|
||||||
@@ -1830,7 +1830,7 @@ public class AiController {
|
|||||||
SpellAbility saGemstones = null;
|
SpellAbility saGemstones = null;
|
||||||
|
|
||||||
List<SpellAbility> toRemove = Lists.newArrayList();
|
List<SpellAbility> toRemove = Lists.newArrayList();
|
||||||
for(SpellAbility sa : result) {
|
for (SpellAbility sa : result) {
|
||||||
String srcName = sa.getHostCard().getName();
|
String srcName = sa.getHostCard().getName();
|
||||||
if ("Gemstone Caverns".equals(srcName)) {
|
if ("Gemstone Caverns".equals(srcName)) {
|
||||||
if (saGemstones == null)
|
if (saGemstones == null)
|
||||||
@@ -2227,11 +2227,11 @@ public class AiController {
|
|||||||
|
|
||||||
private boolean checkAiSpecificRestrictions(final SpellAbility sa) {
|
private boolean checkAiSpecificRestrictions(final SpellAbility sa) {
|
||||||
// AI-specific restrictions specified as activation parameters in spell abilities
|
// AI-specific restrictions specified as activation parameters in spell abilities
|
||||||
|
|
||||||
if (sa.hasParam("AILifeThreshold")) {
|
if (sa.hasParam("AILifeThreshold")) {
|
||||||
return player.getLife() > Integer.parseInt(sa.getParam("AILifeThreshold"));
|
return player.getLife() > Integer.parseInt(sa.getParam("AILifeThreshold"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -354,7 +354,6 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
return PaymentDecision.number(c);
|
return PaymentDecision.number(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PaymentDecision visit(CostPutCardToLib cost) {
|
public PaymentDecision visit(CostPutCardToLib cost) {
|
||||||
if (cost.payCostFromSource()) {
|
if (cost.payCostFromSource()) {
|
||||||
|
|||||||
@@ -331,11 +331,14 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Card getCardPreference(final Player ai, final Card activate, final String pref, final CardCollection typeList) {
|
public static Card getCardPreference(final Player ai, final Card activate, final String pref, final CardCollection typeList) {
|
||||||
|
return getCardPreference(ai, activate, pref, typeList, null);
|
||||||
|
}
|
||||||
|
public static Card getCardPreference(final Player ai, final Card activate, final String pref, final CardCollection typeList, SpellAbility sa) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
String prefDef = "";
|
String prefDef = "";
|
||||||
if (activate != null) {
|
if (activate != null) {
|
||||||
prefDef = activate.getSVar("AIPreference");
|
prefDef = activate.getSVar("AIPreference");
|
||||||
final String[] prefGroups = activate.getSVar("AIPreference").split("\\|");
|
final String[] prefGroups = prefDef.split("\\|");
|
||||||
for (String prefGroup : prefGroups) {
|
for (String prefGroup : prefGroups) {
|
||||||
final String[] prefValid = prefGroup.trim().split("\\$");
|
final String[] prefValid = prefGroup.trim().split("\\$");
|
||||||
if (prefValid[0].equals(pref) && !prefValid[1].startsWith("Special:")) {
|
if (prefValid[0].equals(pref) && !prefValid[1].startsWith("Special:")) {
|
||||||
@@ -346,8 +349,8 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
for (String validItem : prefValid[1].split(",")) {
|
for (String validItem : prefValid[1].split(",")) {
|
||||||
final CardCollection prefList = CardLists.getValidCards(typeList, validItem, activate.getController(), activate, null);
|
final CardCollection prefList = CardLists.getValidCards(typeList, validItem, activate.getController(), activate, null);
|
||||||
int threshold = getAIPreferenceParameter(activate, "CreatureEvalThreshold");
|
int threshold = getAIPreferenceParameter(activate, "CreatureEvalThreshold", sa);
|
||||||
int minNeeded = getAIPreferenceParameter(activate, "MinCreaturesBelowThreshold");
|
int minNeeded = getAIPreferenceParameter(activate, "MinCreaturesBelowThreshold", sa);
|
||||||
|
|
||||||
if (threshold != -1) {
|
if (threshold != -1) {
|
||||||
List<Card> toRemove = Lists.newArrayList();
|
List<Card> toRemove = Lists.newArrayList();
|
||||||
@@ -390,7 +393,7 @@ public class ComputerUtil {
|
|||||||
final CardCollection sacMeList = CardLists.filter(typeList, new Predicate<Card>() {
|
final CardCollection sacMeList = CardLists.filter(typeList, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
return (c.hasSVar("SacMe") && (Integer.parseInt(c.getSVar("SacMe")) == priority));
|
return c.hasSVar("SacMe") && (Integer.parseInt(c.getSVar("SacMe")) == priority);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!sacMeList.isEmpty()) {
|
if (!sacMeList.isEmpty()) {
|
||||||
@@ -419,6 +422,7 @@ public class ComputerUtil {
|
|||||||
if (!nonCreatures.isEmpty()) {
|
if (!nonCreatures.isEmpty()) {
|
||||||
return ComputerUtilCard.getWorstAI(nonCreatures);
|
return ComputerUtilCard.getWorstAI(nonCreatures);
|
||||||
} else if (!typeList.isEmpty()) {
|
} else if (!typeList.isEmpty()) {
|
||||||
|
// TODO make sure survival is possible in case the creature blocks a trampler
|
||||||
return ComputerUtilCard.getWorstAI(typeList);
|
return ComputerUtilCard.getWorstAI(typeList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -505,7 +509,7 @@ public class ComputerUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getAIPreferenceParameter(final Card c, final String paramName) {
|
public static int getAIPreferenceParameter(final Card c, final String paramName, SpellAbility sa) {
|
||||||
if (!c.hasSVar("AIPreferenceParams")) {
|
if (!c.hasSVar("AIPreferenceParams")) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -520,7 +524,21 @@ public class ComputerUtil {
|
|||||||
case "CreatureEvalThreshold":
|
case "CreatureEvalThreshold":
|
||||||
// Threshold of 150 is just below the level of a 1/1 mana dork or a 2/2 baseline creature with no keywords
|
// Threshold of 150 is just below the level of a 1/1 mana dork or a 2/2 baseline creature with no keywords
|
||||||
if (paramName.equals(parName)) {
|
if (paramName.equals(parName)) {
|
||||||
return Integer.parseInt(parValue);
|
int num = 0;
|
||||||
|
try {
|
||||||
|
num = Integer.parseInt(parValue);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
String[] valParts = StringUtils.split(parValue, "/");
|
||||||
|
CardCollection foundCards = AbilityUtils.getDefinedCards(c, valParts[0], sa);
|
||||||
|
if (!foundCards.isEmpty()) {
|
||||||
|
num = ComputerUtilCard.evaluateCreature(foundCards.get(0));
|
||||||
|
}
|
||||||
|
valParts[0] = Integer.toString(num);
|
||||||
|
if (valParts.length > 1) {
|
||||||
|
num = AbilityUtils.doXMath(num, valParts[1], c, sa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "MinCreaturesBelowThreshold":
|
case "MinCreaturesBelowThreshold":
|
||||||
@@ -543,9 +561,8 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
typeList = CardLists.filter(typeList, CardPredicates.canBeSacrificedBy(ability));
|
typeList = CardLists.filter(typeList, CardPredicates.canBeSacrificedBy(ability));
|
||||||
|
|
||||||
if ((target != null) && target.getController() == ai) {
|
// don't sacrifice the card we're pumping
|
||||||
typeList.remove(target); // don't sacrifice the card we're pumping
|
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, ability, ai);
|
||||||
}
|
|
||||||
|
|
||||||
if (typeList.size() < amount) {
|
if (typeList.size() < amount) {
|
||||||
return null;
|
return null;
|
||||||
@@ -573,9 +590,8 @@ public class ComputerUtil {
|
|||||||
final Card target, final int amount, SpellAbility sa) {
|
final Card target, final int amount, SpellAbility sa) {
|
||||||
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, sa);
|
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, sa);
|
||||||
|
|
||||||
if ((target != null) && target.getController() == ai) {
|
// don't exile the card we're pumping
|
||||||
typeList.remove(target); // don't exile the card we're pumping
|
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, sa, ai);
|
||||||
}
|
|
||||||
|
|
||||||
if (typeList.size() < amount) {
|
if (typeList.size() < amount) {
|
||||||
return null;
|
return null;
|
||||||
@@ -594,9 +610,8 @@ public class ComputerUtil {
|
|||||||
final Card target, final int amount, SpellAbility sa) {
|
final Card target, final int amount, SpellAbility sa) {
|
||||||
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, sa);
|
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, sa);
|
||||||
|
|
||||||
if ((target != null) && target.getController() == ai) {
|
// don't move the card we're pumping
|
||||||
typeList.remove(target); // don't move the card we're pumping
|
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, sa, ai);
|
||||||
}
|
|
||||||
|
|
||||||
if (typeList.size() < amount) {
|
if (typeList.size() < amount) {
|
||||||
return null;
|
return null;
|
||||||
@@ -718,12 +733,10 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
final 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);
|
|
||||||
if ((target != null) && target.getController() == ai) {
|
// don't bounce the card we're pumping
|
||||||
// don't bounce the card we're pumping
|
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, sa, ai);
|
||||||
typeList.remove(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeList.size() < amount) {
|
if (typeList.size() < amount) {
|
||||||
return new CardCollection();
|
return new CardCollection();
|
||||||
@@ -743,7 +756,7 @@ public class ComputerUtil {
|
|||||||
CardCollection remaining = new CardCollection(cardlist);
|
CardCollection remaining = new CardCollection(cardlist);
|
||||||
final CardCollection sacrificed = new CardCollection();
|
final CardCollection sacrificed = new CardCollection();
|
||||||
final Card host = source.getHostCard();
|
final Card host = source.getHostCard();
|
||||||
final int considerSacThreshold = getAIPreferenceParameter(host, "CreatureEvalThreshold");
|
final int considerSacThreshold = getAIPreferenceParameter(host, "CreatureEvalThreshold", source);
|
||||||
|
|
||||||
if ("OpponentOnly".equals(source.getParam("AILogic"))) {
|
if ("OpponentOnly".equals(source.getParam("AILogic"))) {
|
||||||
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
|
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
|
||||||
@@ -3026,6 +3039,6 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import forge.game.combat.Combat;
|
|||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.cost.CostPayEnergy;
|
import forge.game.cost.CostPayEnergy;
|
||||||
|
import forge.game.cost.CostRemoveCounter;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.keyword.KeywordCollection;
|
import forge.game.keyword.KeywordCollection;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
@@ -137,6 +138,56 @@ public class ComputerUtilCard {
|
|||||||
return Aggregates.itemWithMin(all, CardPredicates.Accessors.fnGetCmc);
|
return Aggregates.itemWithMin(all, CardPredicates.Accessors.fnGetCmc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Card getBestPlaneswalkerToDamage(final List<Card> pws) {
|
||||||
|
Card bestTgt = null;
|
||||||
|
|
||||||
|
// As of right now, ranks planeswalkers by their Current Loyalty * 10 + Big buff if close to "Ultimate"
|
||||||
|
int bestScore = 0;
|
||||||
|
for (Card pw : pws) {
|
||||||
|
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
||||||
|
int pwScore = curLoyalty * 10;
|
||||||
|
|
||||||
|
for (SpellAbility sa : pw.getSpellAbilities()) {
|
||||||
|
if (sa.hasParam("Ultimate")) {
|
||||||
|
Integer loyaltyCost = 0;
|
||||||
|
CostRemoveCounter remLoyalty = sa.getPayCosts().getCostPartByType(CostRemoveCounter.class);
|
||||||
|
if (remLoyalty != null) {
|
||||||
|
// if remLoyalty is null, generally there's an AddCounter<0/LOYALTY> cost, like for Gideon Jura.
|
||||||
|
loyaltyCost = remLoyalty.convertAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loyaltyCost != null && loyaltyCost != 0 && loyaltyCost - curLoyalty <= 1) {
|
||||||
|
// Will ultimate soon
|
||||||
|
pwScore += 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pwScore > bestScore) {
|
||||||
|
bestScore = pwScore;
|
||||||
|
bestTgt = pw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestTgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Card getWorstPlaneswalkerToDamage(final List<Card> pws) {
|
||||||
|
Card bestTgt = null;
|
||||||
|
|
||||||
|
int bestScore = Integer.MAX_VALUE;
|
||||||
|
for (Card pw : pws) {
|
||||||
|
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
||||||
|
|
||||||
|
if (curLoyalty < bestScore) {
|
||||||
|
bestScore = curLoyalty;
|
||||||
|
bestTgt = pw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestTgt;
|
||||||
|
}
|
||||||
|
|
||||||
// The AI doesn't really pick the best enchantment, just the most expensive.
|
// The AI doesn't really pick the best enchantment, just the most expensive.
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -217,6 +268,63 @@ public class ComputerUtilCard {
|
|||||||
return Aggregates.random(bLand); // random tapped land of least represented type
|
return Aggregates.random(bLand); // random tapped land of least represented type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* getWorstLand.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param lands
|
||||||
|
* @return a {@link forge.game.card.Card} object.
|
||||||
|
*/
|
||||||
|
public static Card getWorstLand(final List<Card> lands) {
|
||||||
|
Card worstLand = null;
|
||||||
|
int maxScore = Integer.MIN_VALUE;
|
||||||
|
// first, check for tapped, basic lands
|
||||||
|
for (Card tmp : lands) {
|
||||||
|
int score = tmp.isTapped() ? 2 : 0;
|
||||||
|
score += tmp.isBasicLand() ? 1 : 0;
|
||||||
|
score -= tmp.isCreature() ? 4 : 0;
|
||||||
|
for (Card aura : tmp.getEnchantedBy()) {
|
||||||
|
if (aura.getController().isOpponentOf(tmp.getController())) {
|
||||||
|
score += 5;
|
||||||
|
} else {
|
||||||
|
score -= 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (score == maxScore &&
|
||||||
|
CardLists.count(lands, CardPredicates.sharesNameWith(tmp)) > CardLists.count(lands, CardPredicates.sharesNameWith(worstLand))) {
|
||||||
|
worstLand = tmp;
|
||||||
|
}
|
||||||
|
if (score > maxScore) {
|
||||||
|
worstLand = tmp;
|
||||||
|
maxScore = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return worstLand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Card getBestLandToAnimate(final Iterable<Card> lands) {
|
||||||
|
Card land = null;
|
||||||
|
int maxScore = Integer.MIN_VALUE;
|
||||||
|
// first, check for tapped, basic lands
|
||||||
|
for (Card tmp : lands) {
|
||||||
|
int score = tmp.isTapped() ? 0 : 2;
|
||||||
|
score += tmp.isBasicLand() ? 2 : 0;
|
||||||
|
score -= tmp.isCreature() ? 4 : 0;
|
||||||
|
score -= 5 * tmp.getEnchantedBy().size();
|
||||||
|
|
||||||
|
if (score == maxScore &&
|
||||||
|
CardLists.count(lands, CardPredicates.sharesNameWith(tmp)) > CardLists.count(lands, CardPredicates.sharesNameWith(land))) {
|
||||||
|
land = tmp;
|
||||||
|
}
|
||||||
|
if (score > maxScore) {
|
||||||
|
land = tmp;
|
||||||
|
maxScore = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return land;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getCheapestPermanentAI.
|
* getCheapestPermanentAI.
|
||||||
@@ -825,57 +933,6 @@ public class ComputerUtilCard {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* getWorstLand.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param lands
|
|
||||||
* @return a {@link forge.game.card.Card} object.
|
|
||||||
*/
|
|
||||||
public static Card getWorstLand(final List<Card> lands) {
|
|
||||||
Card worstLand = null;
|
|
||||||
int maxScore = Integer.MIN_VALUE;
|
|
||||||
// first, check for tapped, basic lands
|
|
||||||
for (Card tmp : lands) {
|
|
||||||
int score = tmp.isTapped() ? 2 : 0;
|
|
||||||
score += tmp.isBasicLand() ? 1 : 0;
|
|
||||||
score -= tmp.isCreature() ? 4 : 0;
|
|
||||||
for (Card aura : tmp.getEnchantedBy()) {
|
|
||||||
if (aura.getController().isOpponentOf(tmp.getController())) {
|
|
||||||
score += 5;
|
|
||||||
} else {
|
|
||||||
score -= 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (score >= maxScore) {
|
|
||||||
worstLand = tmp;
|
|
||||||
maxScore = score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return worstLand;
|
|
||||||
} // end getWorstLand
|
|
||||||
|
|
||||||
public static Card getBestLandToAnimate(final Iterable<Card> lands) {
|
|
||||||
Card land = null;
|
|
||||||
int maxScore = Integer.MIN_VALUE;
|
|
||||||
// first, check for tapped, basic lands
|
|
||||||
for (Card tmp : lands) {
|
|
||||||
// TODO Improve this by choosing basic lands that I have plenty of mana in
|
|
||||||
int score = tmp.isTapped() ? 0 : 2;
|
|
||||||
score += tmp.isBasicLand() ? 2 : 0;
|
|
||||||
score -= tmp.isCreature() ? 4 : 0;
|
|
||||||
score -= 5 * tmp.getEnchantedBy().size();
|
|
||||||
|
|
||||||
if (score >= maxScore) {
|
|
||||||
land = tmp;
|
|
||||||
maxScore = score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return land;
|
|
||||||
} // end getBestLandToAnimate
|
|
||||||
|
|
||||||
public static final Predicate<Deck> AI_KNOWS_HOW_TO_PLAY_ALL_CARDS = new Predicate<Deck>() {
|
public static final Predicate<Deck> AI_KNOWS_HOW_TO_PLAY_ALL_CARDS = new Predicate<Deck>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Deck d) {
|
public boolean apply(Deck d) {
|
||||||
@@ -888,6 +945,7 @@ public class ComputerUtilCard {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static List<String> chooseColor(SpellAbility sa, int min, int max, List<String> colorChoices) {
|
public static List<String> chooseColor(SpellAbility sa, int min, int max, List<String> colorChoices) {
|
||||||
List<String> chosen = new ArrayList<>();
|
List<String> chosen = new ArrayList<>();
|
||||||
Player ai = sa.getActivatingPlayer();
|
Player ai = sa.getActivatingPlayer();
|
||||||
@@ -1592,7 +1650,7 @@ public class ComputerUtilCard {
|
|||||||
*/
|
*/
|
||||||
public static Card getPumpedCreature(final Player ai, final SpellAbility sa,
|
public static Card getPumpedCreature(final Player ai, final SpellAbility sa,
|
||||||
final Card c, int toughness, int power, final List<String> keywords) {
|
final Card c, int toughness, int power, final List<String> keywords) {
|
||||||
Card pumped = CardFactory.copyCard(c, true);
|
Card pumped = CardFactory.copyCard(c, false);
|
||||||
pumped.setSickness(c.hasSickness());
|
pumped.setSickness(c.hasSickness());
|
||||||
final long timestamp = c.getGame().getNextTimestamp();
|
final long timestamp = c.getGame().getNextTimestamp();
|
||||||
final List<String> kws = new ArrayList<>();
|
final List<String> kws = new ArrayList<>();
|
||||||
@@ -1630,7 +1688,7 @@ public class ComputerUtilCard {
|
|||||||
pumped.addChangedCardKeywords(kws, null, false, false, timestamp);
|
pumped.addChangedCardKeywords(kws, null, false, false, timestamp);
|
||||||
Set<CounterType> types = c.getCounters().keySet();
|
Set<CounterType> types = c.getCounters().keySet();
|
||||||
for(CounterType ct : types) {
|
for(CounterType ct : types) {
|
||||||
pumped.addCounterFireNoEvents(ct, c.getCounters(ct), ai, true, null);
|
pumped.addCounterFireNoEvents(ct, c.getCounters(ct), ai, sa, true, null);
|
||||||
}
|
}
|
||||||
//Copies tap-state and extra keywords (auras, equipment, etc.)
|
//Copies tap-state and extra keywords (auras, equipment, etc.)
|
||||||
if (c.isTapped()) {
|
if (c.isTapped()) {
|
||||||
|
|||||||
@@ -472,7 +472,6 @@ public class ComputerUtilCombat {
|
|||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public static boolean wouldLoseLife(final Player ai, final Combat combat) {
|
public static boolean wouldLoseLife(final Player ai, final Combat combat) {
|
||||||
|
|
||||||
return (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) < ai.getLife());
|
return (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) < ai.getLife());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import org.apache.commons.lang3.ObjectUtils;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.base.Predicates;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
@@ -20,6 +22,7 @@ import forge.game.card.CardCollection;
|
|||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardFactoryUtil;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.card.CounterEnumType;
|
import forge.game.card.CounterEnumType;
|
||||||
@@ -270,7 +273,10 @@ public class ComputerUtilCost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final CardCollection sacList = new CardCollection();
|
final CardCollection sacList = new CardCollection();
|
||||||
final CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sourceAbility);
|
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sourceAbility);
|
||||||
|
|
||||||
|
// don't sacrifice the card we're pumping
|
||||||
|
typeList = paymentChoicesWithoutTargets(typeList, sourceAbility, ai);
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (count < amount) {
|
while (count < amount) {
|
||||||
@@ -320,11 +326,14 @@ public class ComputerUtilCost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final CardCollection sacList = new CardCollection();
|
final CardCollection sacList = new CardCollection();
|
||||||
final CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sourceAbility);
|
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sourceAbility);
|
||||||
|
|
||||||
|
// don't sacrifice the card we're pumping
|
||||||
|
typeList = paymentChoicesWithoutTargets(typeList, sourceAbility, ai);
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (count < amount) {
|
while (count < amount) {
|
||||||
Card prefCard = ComputerUtil.getCardPreference(ai, source, "SacCost", typeList);
|
Card prefCard = ComputerUtil.getCardPreference(ai, source, "SacCost", typeList, sourceAbility);
|
||||||
if (prefCard == null) {
|
if (prefCard == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -337,6 +346,19 @@ public class ComputerUtilCost {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check sacrifice cost.
|
||||||
|
*
|
||||||
|
* @param cost
|
||||||
|
* the cost
|
||||||
|
* @param source
|
||||||
|
* the source
|
||||||
|
* @return true, if successful
|
||||||
|
*/
|
||||||
|
public static boolean checkSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility) {
|
||||||
|
return checkSacrificeCost(ai, cost, source, sourceAbility, true);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isSacrificeSelfCost(final Cost cost) {
|
public static boolean isSacrificeSelfCost(final Cost cost) {
|
||||||
if (cost == null) {
|
if (cost == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -397,19 +419,6 @@ public class ComputerUtilCost {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check sacrifice cost.
|
|
||||||
*
|
|
||||||
* @param cost
|
|
||||||
* the cost
|
|
||||||
* @param source
|
|
||||||
* the source
|
|
||||||
* @return true, if successful
|
|
||||||
*/
|
|
||||||
public static boolean checkSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility) {
|
|
||||||
return checkSacrificeCost(ai, cost, source, sourceAbility,true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* shouldPayCost.
|
* shouldPayCost.
|
||||||
@@ -420,8 +429,8 @@ public class ComputerUtilCost {
|
|||||||
* @param cost
|
* @param cost
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static boolean shouldPayCost(final Player ai, final Card hostCard, final Cost cost) {
|
public static boolean shouldPayCost(final Player ai, final Card hostCard, final Cost cost) {
|
||||||
|
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (part instanceof CostPayLife) {
|
if (part instanceof CostPayLife) {
|
||||||
if (!ai.cantLoseForZeroOrLessLife()) {
|
if (!ai.cantLoseForZeroOrLessLife()) {
|
||||||
@@ -741,4 +750,12 @@ public class ComputerUtilCost {
|
|||||||
}
|
}
|
||||||
return ObjectUtils.defaultIfNull(val, 0);
|
return ObjectUtils.defaultIfNull(val, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CardCollection paymentChoicesWithoutTargets(Iterable<Card> choices, SpellAbility source, Player ai) {
|
||||||
|
if (source.usesTargeting()) {
|
||||||
|
final CardCollection targets = new CardCollection(source.getTargets().getTargetCards());
|
||||||
|
choices = Iterables.filter(choices, Predicates.not(Predicates.and(CardPredicates.isController(ai), Predicates.in(targets))));
|
||||||
|
}
|
||||||
|
return new CardCollection(choices);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -788,7 +788,6 @@ public class ComputerUtilMana {
|
|||||||
String manaProduced = ignoreColor || ignoreType ? MagicColor.toShortString(toPay.getColorMask())
|
String manaProduced = ignoreColor || ignoreType ? MagicColor.toShortString(toPay.getColorMask())
|
||||||
: predictManafromSpellAbility(saPayment, ai, toPay);
|
: predictManafromSpellAbility(saPayment, ai, toPay);
|
||||||
|
|
||||||
// System.out.println(manaProduced);
|
|
||||||
payMultipleMana(cost, manaProduced, ai);
|
payMultipleMana(cost, manaProduced, ai);
|
||||||
|
|
||||||
// remove from available lists
|
// remove from available lists
|
||||||
@@ -1072,7 +1071,6 @@ public class ComputerUtilMana {
|
|||||||
getComboManaChoice(ai, saPayment, sa, cost);
|
getComboManaChoice(ai, saPayment, sa, cost);
|
||||||
}
|
}
|
||||||
else if (saPayment.getApi() == ApiType.ManaReflected) {
|
else if (saPayment.getApi() == ApiType.ManaReflected) {
|
||||||
//System.out.println("Evaluate reflected mana of: " + saPayment.getHostCard());
|
|
||||||
Set<String> reflected = CardUtil.getReflectableManaColors(saPayment);
|
Set<String> reflected = CardUtil.getReflectableManaColors(saPayment);
|
||||||
|
|
||||||
for (byte c : MagicColor.WUBRG) {
|
for (byte c : MagicColor.WUBRG) {
|
||||||
@@ -1281,7 +1279,7 @@ public class ComputerUtilMana {
|
|||||||
final AbilityManaPart abMana = manaAb.getManaPart();
|
final AbilityManaPart abMana = manaAb.getManaPart();
|
||||||
|
|
||||||
if (abMana.isComboMana()) {
|
if (abMana.isComboMana()) {
|
||||||
int amount = manaAb.hasParam("Amount") ? AbilityUtils.calculateAmount(source, manaAb.getParam("Amount"), saRoot) : 1;
|
int amount = manaAb.hasParam("Amount") ? AbilityUtils.calculateAmount(source, manaAb.getParam("Amount"), manaAb) : 1;
|
||||||
final ManaCostBeingPaid testCost = new ManaCostBeingPaid(cost);
|
final ManaCostBeingPaid testCost = new ManaCostBeingPaid(cost);
|
||||||
final String[] comboColors = abMana.getComboColors().split(" ");
|
final String[] comboColors = abMana.getComboColors().split(" ");
|
||||||
for (int nMana = 1; nMana <= amount; nMana++) {
|
for (int nMana = 1; nMana <= amount; nMana++) {
|
||||||
@@ -1301,7 +1299,7 @@ public class ComputerUtilMana {
|
|||||||
if (!testCost.isPaid()) {
|
if (!testCost.isPaid()) {
|
||||||
// Loop over combo colors
|
// Loop over combo colors
|
||||||
for (String color : comboColors) {
|
for (String color : comboColors) {
|
||||||
if (satisfiesColorChoice(abMana, choiceString, choice) && testCost.isAnyPartPayableWith(ManaAtom.fromName(color), ai.getManaPool())) {
|
if (satisfiesColorChoice(abMana, choiceString, choice) && testCost.needsColor(ManaAtom.fromName(color), ai.getManaPool())) {
|
||||||
payMultipleMana(testCost, color, ai);
|
payMultipleMana(testCost, color, ai);
|
||||||
if (nMana != 1) {
|
if (nMana != 1) {
|
||||||
choiceString.append(" ");
|
choiceString.append(" ");
|
||||||
@@ -1880,7 +1878,7 @@ public class ComputerUtilMana {
|
|||||||
final Card offering = sa.getSacrificedAsOffering();
|
final Card offering = sa.getSacrificedAsOffering();
|
||||||
offering.setUsedToPay(false);
|
offering.setUsedToPay(false);
|
||||||
if (costIsPaid && !test) {
|
if (costIsPaid && !test) {
|
||||||
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, null);
|
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, null, null);
|
||||||
}
|
}
|
||||||
sa.resetSacrificedAsOffering();
|
sa.resetSacrificedAsOffering();
|
||||||
}
|
}
|
||||||
@@ -1888,7 +1886,7 @@ public class ComputerUtilMana {
|
|||||||
final Card emerge = sa.getSacrificedAsEmerge();
|
final Card emerge = sa.getSacrificedAsEmerge();
|
||||||
emerge.setUsedToPay(false);
|
emerge.setUsedToPay(false);
|
||||||
if (costIsPaid && !test) {
|
if (costIsPaid && !test) {
|
||||||
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, null);
|
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, null, null);
|
||||||
}
|
}
|
||||||
sa.resetSacrificedAsEmerge();
|
sa.resetSacrificedAsEmerge();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ public abstract class GameState {
|
|||||||
ZONES.put(ZoneType.Library, "library");
|
ZONES.put(ZoneType.Library, "library");
|
||||||
ZONES.put(ZoneType.Exile, "exile");
|
ZONES.put(ZoneType.Exile, "exile");
|
||||||
ZONES.put(ZoneType.Command, "command");
|
ZONES.put(ZoneType.Command, "command");
|
||||||
|
ZONES.put(ZoneType.Sideboard, "sideboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
private int humanLife = -1;
|
private int humanLife = -1;
|
||||||
@@ -398,6 +399,12 @@ public abstract class GameState {
|
|||||||
// Need to figure out a better way to detect if it's actually on adventure.
|
// Need to figure out a better way to detect if it's actually on adventure.
|
||||||
newText.append("|OnAdventure");
|
newText.append("|OnAdventure");
|
||||||
}
|
}
|
||||||
|
if (c.isForetold()) {
|
||||||
|
newText.append("|Foretold");
|
||||||
|
}
|
||||||
|
if (c.isForetoldThisTurn()) {
|
||||||
|
newText.append("|ForetoldThisTurn");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,6 +572,13 @@ public abstract class GameState {
|
|||||||
aiCardTexts.put(ZoneType.Command, categoryValue);
|
aiCardTexts.put(ZoneType.Command, categoryValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (categoryName.endsWith("sideboard")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanCardTexts.put(ZoneType.Sideboard, categoryValue);
|
||||||
|
else
|
||||||
|
aiCardTexts.put(ZoneType.Sideboard, categoryValue);
|
||||||
|
}
|
||||||
|
|
||||||
else if (categoryName.startsWith("ability")) {
|
else if (categoryName.startsWith("ability")) {
|
||||||
abilityString.put(categoryName.substring("ability".length()), categoryValue);
|
abilityString.put(categoryName.substring("ability".length()), categoryValue);
|
||||||
}
|
}
|
||||||
@@ -1175,7 +1189,7 @@ public abstract class GameState {
|
|||||||
String[] allCounterStrings = counterString.split(",");
|
String[] allCounterStrings = counterString.split(",");
|
||||||
for (final String counterPair : allCounterStrings) {
|
for (final String counterPair : allCounterStrings) {
|
||||||
String[] pair = counterPair.split("=", 2);
|
String[] pair = counterPair.split("=", 2);
|
||||||
entity.addCounter(CounterType.getType(pair[0]), Integer.parseInt(pair[1]), null, false, false, null);
|
entity.addCounter(CounterType.getType(pair[0]), Integer.parseInt(pair[1]), null, null, false, false, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1391,6 +1405,14 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
} else if (info.equals("NoETBTrigs")) {
|
} else if (info.equals("NoETBTrigs")) {
|
||||||
cardsWithoutETBTrigs.add(c);
|
cardsWithoutETBTrigs.add(c);
|
||||||
|
} else if (info.equals("Foretold")) {
|
||||||
|
c.setForetold(true);
|
||||||
|
c.turnFaceDown(true);
|
||||||
|
c.addMayLookTemp(c.getOwner());
|
||||||
|
} else if (info.equals("ForetoldThisTurn")) {
|
||||||
|
c.setForetoldThisTurn(true);
|
||||||
|
} else if (info.equals("IsToken")) {
|
||||||
|
c.setToken(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,23 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
return new HashMap<>();
|
return new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Byte, Integer> specifyManaCombo(SpellAbility sa, ColorSet colorSet, int manaAmount, boolean different) {
|
||||||
|
Map<Byte, Integer> result = new HashMap<>();
|
||||||
|
for (int i = 0; i < manaAmount; ++i) {
|
||||||
|
Byte chosen = chooseColor("", sa, colorSet);
|
||||||
|
if (result.containsKey(chosen)) {
|
||||||
|
result.put(chosen, result.get(chosen) + 1);
|
||||||
|
} else {
|
||||||
|
result.put(chosen, 1);
|
||||||
|
}
|
||||||
|
if (different) {
|
||||||
|
colorSet = ColorSet.fromMask(colorSet.getColor() - chosen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer announceRequirements(SpellAbility ability, String announce) {
|
public Integer announceRequirements(SpellAbility ability, String announce) {
|
||||||
// For now, these "announcements" are made within the AI classes of the appropriate SA effects
|
// For now, these "announcements" are made within the AI classes of the appropriate SA effects
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ public class SpecialCardAi {
|
|||||||
|
|
||||||
Card animated = AnimateAi.becomeAnimated(sa.getHostCard(), sa.getSubAbility());
|
Card animated = AnimateAi.becomeAnimated(sa.getHostCard(), sa.getSubAbility());
|
||||||
if (sa.getHostCard().canReceiveCounters(CounterEnumType.P1P1)) {
|
if (sa.getHostCard().canReceiveCounters(CounterEnumType.P1P1)) {
|
||||||
animated.addCounter(CounterEnumType.P1P1, 2, ai, false, null);
|
animated.addCounter(CounterEnumType.P1P1, 2, ai, sa.getSubAbility(), false, null);
|
||||||
}
|
}
|
||||||
boolean isOppEOT = ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == ai;
|
boolean isOppEOT = ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == ai;
|
||||||
boolean isValuableAttacker = ph.is(PhaseType.MAIN1, ai) && ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, animated);
|
boolean isValuableAttacker = ph.is(PhaseType.MAIN1, ai) && ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, animated);
|
||||||
|
|||||||
@@ -289,7 +289,6 @@ public abstract class SpellAbilityAi {
|
|||||||
if (sa.isSpell() && !sa.isBuyBackAbility()) {
|
if (sa.isSpell() && !sa.isBuyBackAbility()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai);
|
return phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ public enum SpellApiToAi {
|
|||||||
.put(ApiType.UnattachAll, UnattachAllAi.class)
|
.put(ApiType.UnattachAll, UnattachAllAi.class)
|
||||||
.put(ApiType.Untap, UntapAi.class)
|
.put(ApiType.Untap, UntapAi.class)
|
||||||
.put(ApiType.UntapAll, UntapAllAi.class)
|
.put(ApiType.UntapAll, UntapAllAi.class)
|
||||||
|
.put(ApiType.Venture, VentureAi.class)
|
||||||
.put(ApiType.Vote, VoteAi.class)
|
.put(ApiType.Vote, VoteAi.class)
|
||||||
.put(ApiType.WinsGame, GameWinAi.class)
|
.put(ApiType.WinsGame, GameWinAi.class)
|
||||||
|
|
||||||
@@ -191,6 +192,6 @@ public enum SpellApiToAi {
|
|||||||
result = ReflectionUtil.makeDefaultInstanceOf(clz);
|
result = ReflectionUtil.makeDefaultInstanceOf(clz);
|
||||||
apiToInstance.put(api, result);
|
apiToInstance.put(api, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -984,7 +984,7 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
List<GameObject> targets = new ArrayList<>();
|
List<GameObject> targets = new ArrayList<>();
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
targets = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
|
targets = AbilityUtils.getDefinedObjects(card, sa.getParam("Defined"), sa);
|
||||||
} else {
|
} else {
|
||||||
AttachAi.attachPreference(sa, tgt, mandatory);
|
AttachAi.attachPreference(sa, tgt, mandatory);
|
||||||
targets = sa.getTargets();
|
targets = sa.getTargets();
|
||||||
@@ -1344,7 +1344,7 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
CardCollection list = null;
|
CardCollection list = null;
|
||||||
if (tgt == null) {
|
if (tgt == null) {
|
||||||
list = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
list = AbilityUtils.getDefinedCards(attachSource, sa.getParam("Defined"), sa);
|
||||||
} else {
|
} else {
|
||||||
list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), sa.getActivatingPlayer(), attachSource, sa);
|
list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), sa.getActivatingPlayer(), attachSource, sa);
|
||||||
|
|
||||||
|
|||||||
@@ -1709,7 +1709,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||||
// Called when looking for creature to attach aura or equipment
|
// Called when looking for creature to attach aura or equipment
|
||||||
|
|||||||
@@ -23,21 +23,23 @@ import forge.util.collect.FCollection;
|
|||||||
public class CharmAi extends SpellAbilityAi {
|
public class CharmAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
// sa is Entwined, no need for extra logic
|
|
||||||
if (sa.isEntwine()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
|
||||||
|
|
||||||
final int num = AbilityUtils.calculateAmount(source, sa.getParamOrDefault("CharmNum", "1"), sa);
|
final int num;
|
||||||
final int min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParamOrDefault("MinCharmNum", "1"), sa) : num;
|
final int min;
|
||||||
|
if (sa.isEntwine()) {
|
||||||
|
num = min = choices.size();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
num = AbilityUtils.calculateAmount(source, sa.getParamOrDefault("CharmNum", "1"), sa);
|
||||||
|
min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParamOrDefault("MinCharmNum", "1"), sa) : num;
|
||||||
|
}
|
||||||
|
|
||||||
boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now?
|
boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now?
|
||||||
|
|
||||||
// Reset the chosen list otherwise it will be locked in forever by earlier calls
|
// Reset the chosen list otherwise it will be locked in forever by earlier calls
|
||||||
sa.setChosenList(null);
|
sa.setChosenList(null);
|
||||||
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
|
|
||||||
List<AbilitySub> chosenList;
|
List<AbilitySub> chosenList;
|
||||||
|
|
||||||
if (!ai.equals(sa.getActivatingPlayer())) {
|
if (!ai.equals(sa.getActivatingPlayer())) {
|
||||||
@@ -159,7 +161,7 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
chosenList.add(allyTainted ? gain : lose);
|
chosenList.add(allyTainted ? gain : lose);
|
||||||
} else if (oppTainted || ai.getGame().isCardInPlay("Rain of Gore")) {
|
} else if (oppTainted || ai.getGame().isCardInPlay("Rain of Gore")) {
|
||||||
// Rain of Gore does negate lifegain, so don't benefit the others
|
// Rain of Gore does negate lifegain, so don't benefit the others
|
||||||
// same for if a oppoent does control Tainted Remedy
|
// same for if a opponent does control Tainted Remedy
|
||||||
// but if ai cant gain life, the effects are negated
|
// but if ai cant gain life, the effects are negated
|
||||||
chosenList.add(ai.canGainLife() ? lose : gain);
|
chosenList.add(ai.canGainLife() ? lose : gain);
|
||||||
} else if (ai.getGame().isCardInPlay("Sulfuric Vortex")) {
|
} else if (ai.getGame().isCardInPlay("Sulfuric Vortex")) {
|
||||||
@@ -177,13 +179,13 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
chosenList.add(gain);
|
chosenList.add(gain);
|
||||||
} else if(!ai.canGainLife() && aiLife == 14 ) {
|
} else if(!ai.canGainLife() && aiLife == 14 ) {
|
||||||
// ai cant gain life, but try to avoid falling to 13
|
// ai cant gain life, but try to avoid falling to 13
|
||||||
// but if a oppoent does control Tainted Remedy its irrelevant
|
// but if a opponent does control Tainted Remedy its irrelevant
|
||||||
chosenList.add(oppTainted ? lose : gain);
|
chosenList.add(oppTainted ? lose : gain);
|
||||||
} else if (allyTainted) {
|
} else if (allyTainted) {
|
||||||
// Tainted Remedy negation logic, try gain instead of lose
|
// Tainted Remedy negation logic, try gain instead of lose
|
||||||
// because negation does turn it into lose for opponents
|
// because negation does turn it into lose for opponents
|
||||||
boolean oppCritical = false;
|
boolean oppCritical = false;
|
||||||
// an oppoent is Critical = 14, and can't gain life, try to lose life instead
|
// an opponent is Critical = 14, and can't gain life, try to lose life instead
|
||||||
// but only if ai doesn't kill itself with that.
|
// but only if ai doesn't kill itself with that.
|
||||||
if (aiLife != 14) {
|
if (aiLife != 14) {
|
||||||
for (Player p : opponents) {
|
for (Player p : opponents) {
|
||||||
@@ -197,7 +199,7 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
} else {
|
} else {
|
||||||
// normal logic, try to gain life if its critical
|
// normal logic, try to gain life if its critical
|
||||||
boolean oppCritical = false;
|
boolean oppCritical = false;
|
||||||
// an oppoent is Critical = 12, and can gain life, try to gain life instead
|
// an opponent is Critical = 12, and can gain life, try to gain life instead
|
||||||
// but only if ai doesn't kill itself with that.
|
// but only if ai doesn't kill itself with that.
|
||||||
if (aiLife != 12) {
|
if (aiLife != 12) {
|
||||||
for (Player p : opponents) {
|
for (Player p : opponents) {
|
||||||
@@ -224,6 +226,8 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
goodChoice = sub;
|
goodChoice = sub;
|
||||||
} else {
|
} else {
|
||||||
// Standard canPlayAi()
|
// Standard canPlayAi()
|
||||||
|
sub.setActivatingPlayer(ai);
|
||||||
|
sub.getRestrictions().setZone(sub.getParent().getRestrictions().getZone());
|
||||||
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
|
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
|
||||||
chosenList.add(sub);
|
chosenList.add(sub);
|
||||||
if (chosenList.size() == min) {
|
if (chosenList.size() == min) {
|
||||||
|
|||||||
@@ -246,7 +246,6 @@ public class ChooseCardAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
System.out.println("Tangle Wire" + options + " - " + betterList);
|
|
||||||
if (!betterList.isEmpty()) {
|
if (!betterList.isEmpty()) {
|
||||||
choice = betterList.get(0);
|
choice = betterList.get(0);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ import forge.util.Aggregates;
|
|||||||
public class ControlGainAi extends SpellAbilityAi {
|
public class ControlGainAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
|
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
|
||||||
|
|
||||||
final List<String> lose = Lists.newArrayList();
|
final List<String> lose = Lists.newArrayList();
|
||||||
|
|
||||||
if (sa.hasParam("LoseControl")) {
|
if (sa.hasParam("LoseControl")) {
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import forge.util.collect.FCollection;
|
|||||||
public class CountersMoveAi extends SpellAbilityAi {
|
public class CountersMoveAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
if (!moveTgtAI(ai, sa)) {
|
if (!moveTgtAI(ai, sa)) {
|
||||||
@@ -83,8 +82,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// something you can't block, try to reduce its
|
// something you can't block, try to reduce its attack
|
||||||
// attack
|
|
||||||
if (!ComputerUtilCard.canBeBlockedProfitably(ai, cpy)) {
|
if (!ComputerUtilCard.canBeBlockedProfitably(ai, cpy)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -119,7 +117,6 @@ public class CountersMoveAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
|
||||||
@@ -237,7 +234,6 @@ public class CountersMoveAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean moveTgtAI(final Player ai, final SpellAbility sa) {
|
private boolean moveTgtAI(final Player ai, final SpellAbility sa) {
|
||||||
|
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
@@ -283,8 +279,7 @@ public class CountersMoveAi extends SpellAbilityAi {
|
|||||||
// cant use substract on Copy
|
// cant use substract on Copy
|
||||||
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
|
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
|
||||||
|
|
||||||
// do not steal a P1P1 from Undying if it would die
|
// do not steal a P1P1 from Undying if it would die this way
|
||||||
// this way
|
|
||||||
if (cType != null && cType.is(CounterEnumType.P1P1) && srcCardCpy.getNetToughness() <= 0) {
|
if (cType != null && cType.is(CounterEnumType.P1P1) && srcCardCpy.getNetToughness() <= 0) {
|
||||||
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken();
|
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken();
|
||||||
}
|
}
|
||||||
@@ -373,8 +368,13 @@ public class CountersMoveAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Card lki = CardUtil.getLKICopy(src);
|
Card lki = CardUtil.getLKICopy(src);
|
||||||
lki.clearCounters();
|
if (cType == null) {
|
||||||
// go for opponent when value implies debuff
|
lki.clearCounters();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lki.setCounters(cType, 0);
|
||||||
|
}
|
||||||
|
// go for opponent when higher value implies debuff
|
||||||
if (ComputerUtilCard.evaluateCreature(src) > ComputerUtilCard.evaluateCreature(lki)) {
|
if (ComputerUtilCard.evaluateCreature(src) > ComputerUtilCard.evaluateCreature(lki)) {
|
||||||
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ai);
|
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ai);
|
||||||
if (!aiList.isEmpty()) {
|
if (!aiList.isEmpty()) {
|
||||||
@@ -419,6 +419,12 @@ public class CountersMoveAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
|
||||||
|
|| (sa.getRootAbility().isTrigger() && !sa.getRootAbility().isOptionalTrigger());
|
||||||
|
if (!isMandatoryTrigger) {
|
||||||
|
// no good target
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// move counter to opponents creature but only if you can not steal them
|
// move counter to opponents creature but only if you can not steal them
|
||||||
|
|||||||
@@ -64,11 +64,9 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
|
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
|
||||||
|
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final String aiLogic = sa.getParamOrDefault("AILogic", "");
|
final String aiLogic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
if (!super.willPayCosts(ai, sa, cost, source)) {
|
if (!super.willPayCosts(ai, sa, cost, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -225,8 +223,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa.canTarget(ai)) {
|
if (sa.canTarget(ai)) {
|
||||||
// don't target itself when its forced to add poison
|
// don't target itself when its forced to add poison counters too
|
||||||
// counters too
|
|
||||||
if (!ai.getCounters().isEmpty()) {
|
if (!ai.getCounters().isEmpty()) {
|
||||||
if (!eachExisting || ai.getPoisonCounters() < 5) {
|
if (!eachExisting || ai.getPoisonCounters() < 5) {
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
@@ -480,7 +477,6 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
|
|
||||||
// don't put the counter on the dead creature
|
// don't put the counter on the dead creature
|
||||||
if (sacSelf && c.equals(source)) {
|
if (sacSelf && c.equals(source)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -493,6 +489,8 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
Card sacTarget = ComputerUtil.getCardPreference(ai, source, "SacCost", list);
|
Card sacTarget = ComputerUtil.getCardPreference(ai, source, "SacCost", list);
|
||||||
// this card is planned to be sacrificed during cost payment, so don't target it
|
// this card is planned to be sacrificed during cost payment, so don't target it
|
||||||
// (otherwise the AI can cheat by activating this SA and not paying the sac cost, e.g. Extruder)
|
// (otherwise the AI can cheat by activating this SA and not paying the sac cost, e.g. Extruder)
|
||||||
|
// TODO needs update if amount > 1 gets printed,
|
||||||
|
// maybe also check putting the counter on that exact creature is more important than sacrificing it (though unlikely?)
|
||||||
list.remove(sacTarget);
|
list.remove(sacTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,7 +615,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
// Instant +1/+1
|
// Instant +1/+1
|
||||||
if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa)) {
|
if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
if (!(ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN) && abCost.isReusuableResource())) {
|
if (!(ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN) && abCost.isReusuableResource())) {
|
||||||
return false; // only if next turn and cost is reusable
|
return false; // only if next turn and cost is reusable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,7 +195,6 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, Map<String, Object> params) {
|
public CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, Map<String, Object> params) {
|
||||||
|
|
||||||
if (options.size() > 1) {
|
if (options.size() > 1) {
|
||||||
final Player ai = sa.getActivatingPlayer();
|
final Player ai = sa.getActivatingPlayer();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ public class CountersRemoveAi extends SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
|
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class DamageAllAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
int x = -1;
|
int x = -1;
|
||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
||||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$Converge")) {
|
if (damage.equals("X") && sa.getSVar(damage).equals("Count$Converge")) {
|
||||||
dmg = ComputerUtilMana.getConvergeCount(sa, ai);
|
dmg = ComputerUtilMana.getConvergeCount(sa, ai);
|
||||||
}
|
}
|
||||||
@@ -202,7 +202,7 @@ public class DamageAllAi extends SpellAbilityAi {
|
|||||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
|
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||||
sa.setXManaCostPaid(dmg);
|
sa.setXManaCostPaid(dmg);
|
||||||
} else {
|
} else {
|
||||||
dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("ValidPlayers")) {
|
if (sa.hasParam("ValidPlayers")) {
|
||||||
@@ -286,7 +286,7 @@ public class DamageAllAi extends SpellAbilityAi {
|
|||||||
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
|
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||||
sa.setXManaCostPaid(dmg);
|
sa.setXManaCostPaid(dmg);
|
||||||
} else {
|
} else {
|
||||||
dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("ValidPlayers")) {
|
if (sa.hasParam("ValidPlayers")) {
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ import forge.game.card.CardPredicates;
|
|||||||
import forge.game.card.CounterEnumType;
|
import forge.game.card.CounterEnumType;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.cost.CostPartMana;
|
import forge.game.cost.CostPartMana;
|
||||||
import forge.game.cost.CostRemoveCounter;
|
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetChoices;
|
import forge.game.spellability.TargetChoices;
|
||||||
@@ -53,10 +53,9 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
|
||||||
final String logic = sa.getParam("AILogic");
|
|
||||||
|
|
||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
|
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
||||||
|
final String logic = sa.getParam("AILogic");
|
||||||
|
|
||||||
if ("MadSarkhanDigDmg".equals(logic)) {
|
if ("MadSarkhanDigDmg".equals(logic)) {
|
||||||
return SpecialCardAi.SarkhanTheMad.considerDig(ai, sa);
|
return SpecialCardAi.SarkhanTheMad.considerDig(ai, sa);
|
||||||
@@ -101,13 +100,12 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
|
|
||||||
final Cost abCost = sa.getPayCosts();
|
final Cost abCost = sa.getPayCosts();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
|
|
||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
||||||
|
|
||||||
if (damage.equals("X")) {
|
if (damage.equals("X")) {
|
||||||
if (sa.getSVar(damage).equals("Count$xPaid") || sourceName.equals("Crater's Claws")) {
|
if (sa.getSVar(damage).equals("Count$xPaid") || sourceName.equals("Crater's Claws")) {
|
||||||
@@ -439,63 +437,12 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
|
|
||||||
// We can hurt a planeswalker, so rank the one which is the best target
|
// We can hurt a planeswalker, so rank the one which is the best target
|
||||||
if (!hPlay.isEmpty() && pl.isOpponentOf(ai) && activator.equals(ai)) {
|
if (!hPlay.isEmpty() && pl.isOpponentOf(ai) && activator.equals(ai)) {
|
||||||
return getBestPlaneswalkerToDamage(hPlay);
|
return ComputerUtilCard.getBestPlaneswalkerToDamage(hPlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Card getBestPlaneswalkerToDamage(final List<Card> pws) {
|
|
||||||
Card bestTgt = null;
|
|
||||||
|
|
||||||
// As of right now, ranks planeswalkers by their Current Loyalty * 10 + Big buff if close to "Ultimate"
|
|
||||||
int bestScore = 0;
|
|
||||||
for (Card pw : pws) {
|
|
||||||
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
|
||||||
int pwScore = curLoyalty * 10;
|
|
||||||
|
|
||||||
for (SpellAbility sa : pw.getSpellAbilities()) {
|
|
||||||
if (sa.hasParam("Ultimate")) {
|
|
||||||
Integer loyaltyCost = 0;
|
|
||||||
CostRemoveCounter remLoyalty = sa.getPayCosts().getCostPartByType(CostRemoveCounter.class);
|
|
||||||
if (remLoyalty != null) {
|
|
||||||
// if remLoyalty is null, generally there's an AddCounter<0/LOYALTY> cost, like for Gideon Jura.
|
|
||||||
loyaltyCost = remLoyalty.convertAmount();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loyaltyCost != null && loyaltyCost != 0 && loyaltyCost - curLoyalty <= 1) {
|
|
||||||
// Will ultimate soon
|
|
||||||
pwScore += 10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pwScore > bestScore) {
|
|
||||||
bestScore = pwScore;
|
|
||||||
bestTgt = pw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestTgt;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Card getWorstPlaneswalkerToDamage(final List<Card> pws) {
|
|
||||||
Card bestTgt = null;
|
|
||||||
|
|
||||||
int bestScore = Integer.MAX_VALUE;
|
|
||||||
for (Card pw : pws) {
|
|
||||||
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
|
|
||||||
|
|
||||||
if (curLoyalty < bestScore) {
|
|
||||||
bestScore = curLoyalty;
|
|
||||||
bestTgt = pw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestTgt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private List<Card> getTargetableCards(Player ai, SpellAbility sa, Player pl, TargetRestrictions tgt, Player activator, Card source, Game game) {
|
private List<Card> getTargetableCards(Player ai, SpellAbility sa, Player pl, TargetRestrictions tgt, Player activator, Card source, Game game) {
|
||||||
List<Card> hPlay = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), activator, source, sa);
|
List<Card> hPlay = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), activator, source, sa);
|
||||||
|
|
||||||
@@ -584,7 +531,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
&& "P1P1".equals(sa.getParent().getParam("CounterType"))) {
|
&& "P1P1".equals(sa.getParent().getParam("CounterType"))) {
|
||||||
// assuming the SA parent is of PutCounter type. Perhaps it's possible to predict counter multipliers here somehow?
|
// assuming the SA parent is of PutCounter type. Perhaps it's possible to predict counter multipliers here somehow?
|
||||||
final String amountStr = sa.getParent().getParamOrDefault("CounterNum", "1");
|
final String amountStr = sa.getParent().getParamOrDefault("CounterNum", "1");
|
||||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
final int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
dmg += amount;
|
dmg += amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -897,7 +844,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
// this is for Triggered targets that are mandatory
|
// this is for Triggered targets that are mandatory
|
||||||
final boolean noPrevention = sa.hasParam("NoPrevention");
|
final boolean noPrevention = sa.hasParam("NoPrevention");
|
||||||
final boolean divided = sa.isDividedAsYouChoose();
|
final boolean divided = sa.isDividedAsYouChoose();
|
||||||
final Player opp = ai.getWeakestOpponent();
|
PlayerCollection opps = ai.getOpponents();
|
||||||
|
|
||||||
while (sa.canAddMoreTarget()) {
|
while (sa.canAddMoreTarget()) {
|
||||||
if (tgt.canTgtPlaneswalker()) {
|
if (tgt.canTgtPlaneswalker()) {
|
||||||
@@ -925,13 +872,17 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.canTarget(opp)) {
|
if (!opps.isEmpty()) {
|
||||||
if (sa.getTargets().add(opp)) {
|
Player opp = opps.getFirst();
|
||||||
if (divided) {
|
opps.remove(opp);
|
||||||
sa.addDividedAllocation(opp, dmg);
|
if (sa.canTarget(opp)) {
|
||||||
break;
|
if (sa.getTargets().add(opp)) {
|
||||||
|
if (divided) {
|
||||||
|
sa.addDividedAllocation(opp, dmg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,7 +901,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
}
|
}
|
||||||
else if (tgt.canTgtPlaneswalker()) {
|
else if (tgt.canTgtPlaneswalker()) {
|
||||||
// Second pass for planeswalkers: choose AI's worst planeswalker
|
// Second pass for planeswalkers: choose AI's worst planeswalker
|
||||||
final Card c = getWorstPlaneswalkerToDamage(CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Predicates.and(CardPredicates.Presets.PLANESWALKERS), CardPredicates.isTargetableBy(sa)));
|
final Card c = ComputerUtilCard.getWorstPlaneswalkerToDamage(CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Predicates.and(CardPredicates.Presets.PLANESWALKERS), CardPredicates.isTargetableBy(sa)));
|
||||||
if (c != null) {
|
if (c != null) {
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import forge.game.zone.ZoneType;
|
|||||||
public class DestroyAi extends SpellAbilityAi {
|
public class DestroyAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
return canPlayAI(ai, sa);
|
return checkApiLogic(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ public class DrawAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
int numCards = 1;
|
int numCards = 1;
|
||||||
if (sa.hasParam("NumCards")) {
|
if (sa.hasParam("NumCards")) {
|
||||||
numCards = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa);
|
numCards = AbilityUtils.calculateAmount(source, sa.getParam("NumCards"), sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean xPaid = false;
|
boolean xPaid = false;
|
||||||
|
|||||||
@@ -67,8 +67,7 @@ public class FightAi extends SpellAbilityAi {
|
|||||||
for (Card humanCreature : humCreatures) {
|
for (Card humanCreature : humCreatures) {
|
||||||
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= fighter1.getNetPower()
|
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= fighter1.getNetPower()
|
||||||
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(fighter1)) {
|
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(fighter1)) {
|
||||||
// todo: check min/max targets; see if we picked the best
|
// todo: check min/max targets; see if we picked the best matchup
|
||||||
// matchup
|
|
||||||
sa.getTargets().add(humanCreature);
|
sa.getTargets().add(humanCreature);
|
||||||
return true;
|
return true;
|
||||||
} else if (humanCreature.getSVar("Targeting").equals("Dies")) {
|
} else if (humanCreature.getSVar("Targeting").equals("Dies")) {
|
||||||
@@ -85,8 +84,7 @@ public class FightAi extends SpellAbilityAi {
|
|||||||
for (Card aiCreature : aiCreatures) {
|
for (Card aiCreature : aiCreatures) {
|
||||||
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower()
|
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower()
|
||||||
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(aiCreature)) {
|
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(aiCreature)) {
|
||||||
// todo: check min/max targets; see if we picked the
|
// todo: check min/max targets; see if we picked the best matchup
|
||||||
// best matchup
|
|
||||||
sa.getTargets().add(humanCreature);
|
sa.getTargets().add(humanCreature);
|
||||||
sa.getTargets().add(aiCreature);
|
sa.getTargets().add(aiCreature);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ public class LifeSetAi extends SpellAbilityAi {
|
|||||||
sa.setXManaCostPaid(xPay);
|
sa.setXManaCostPaid(xPay);
|
||||||
amount = xPay;
|
amount = xPay;
|
||||||
} else {
|
} else {
|
||||||
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
// special cases when amount can't be calculated without targeting first
|
// special cases when amount can't be calculated without targeting first
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class MustBlockAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
Card attacker = null;
|
Card attacker = null;
|
||||||
if (sa.hasParam("DefinedAttacker")) {
|
if (sa.hasParam("DefinedAttacker")) {
|
||||||
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
|
final List<Card> cards = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedAttacker"), sa);
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
|
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
|
|
||||||
if (card.hasKeyword("MayFlashSac") && !ai.couldCastSorcery(sa)) {
|
if (card.hasKeyword("MayFlashSac") && !ai.couldCastSorcery(sa)) {
|
||||||
@@ -51,7 +50,6 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
|
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ public class PermanentCreatureAi extends PermanentAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
|
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
@@ -176,7 +175,6 @@ public class PermanentCreatureAi extends PermanentAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (hasFloatMana || willDiscardNow || willDieNow) {
|
if (hasFloatMana || willDiscardNow || willDieNow) {
|
||||||
// Will lose mana in pool or about to discard a card in cleanup or about to die in combat, so use this opportunity
|
// Will lose mana in pool or about to discard a card in cleanup or about to die in combat, so use this opportunity
|
||||||
return true;
|
return true;
|
||||||
@@ -207,7 +205,6 @@ public class PermanentCreatureAi extends PermanentAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
|
|
||||||
if (!super.checkApiLogic(ai, sa)) {
|
if (!super.checkApiLogic(ai, sa)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ public class PermanentNoncreatureAi extends PermanentAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
|
||||||
|
|
||||||
if (!super.checkApiLogic(ai, sa))
|
if (!super.checkApiLogic(ai, sa))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import forge.game.card.CardPredicates;
|
|||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
|
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.SpellAbilityPredicates;
|
||||||
@@ -63,7 +64,7 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (!sa.hasParam("Valid")) {
|
} else if (!sa.hasParam("Valid")) {
|
||||||
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
cards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -158,6 +159,11 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
*/
|
*/
|
||||||
@@ -192,7 +198,7 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
spell = (Spell) spell.copyWithDefinedCost(abCost);
|
spell = (Spell) spell.copyWithDefinedCost(abCost);
|
||||||
}
|
}
|
||||||
if (AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !isOptional, true)) {
|
if (AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !(isOptional || sa.hasParam("Optional")), true)) {
|
||||||
// Before accepting, see if the spell has a valid number of targets (it should at this point).
|
// Before accepting, see if the spell has a valid number of targets (it should at this point).
|
||||||
// Proceeding past this point if the spell is not correctly targeted will result
|
// Proceeding past this point if the spell is not correctly targeted will result
|
||||||
// in "Failed to add to stack" error and the card disappearing from the game completely.
|
// in "Failed to add to stack" error and the card disappearing from the game completely.
|
||||||
|
|||||||
@@ -202,11 +202,10 @@ public class ProtectAi extends SpellAbilityAi {
|
|||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
CardCollection list = getProtectCreatures(ai, sa);
|
CardCollection list = getProtectCreatures(ai, sa);
|
||||||
|
|
||||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
|
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), source, sa);
|
||||||
|
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
// If the cost is tapping, don't activate before declare
|
// If the cost is tapping, don't activate before declare attack/block
|
||||||
// attack/block
|
|
||||||
if (sa.getPayCosts().hasTapCost()) {
|
if (sa.getPayCosts().hasTapCost()) {
|
||||||
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
&& game.getPhaseHandler().isPlayerTurn(ai)) {
|
&& game.getPhaseHandler().isPlayerTurn(ai)) {
|
||||||
@@ -341,7 +340,7 @@ public class ProtectAi extends SpellAbilityAi {
|
|||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getTargets().size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
if (sa.getTargets().size() < tgt.getMinTargets(source, sa)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,8 +382,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// when this happens we need to expand AI to consider if its ok for
|
// when this happens we need to expand AI to consider if its ok for everything?
|
||||||
// everything?
|
|
||||||
for (final Card card : cards) {
|
for (final Card card : cards) {
|
||||||
if (sa.isCurse()) {
|
if (sa.isCurse()) {
|
||||||
if (!card.getController().isOpponentOf(ai)) {
|
if (!card.getController().isOpponentOf(ai)) {
|
||||||
@@ -488,7 +487,31 @@ public class PumpAi extends PumpAiBase {
|
|||||||
// each player sacrifices one permanent, e.g. Vaevictis, Asmadi the Dire - grab the worst for allied and
|
// each player sacrifices one permanent, e.g. Vaevictis, Asmadi the Dire - grab the worst for allied and
|
||||||
// the best for opponents
|
// the best for opponents
|
||||||
return SacrificeAi.doSacOneEachLogic(ai, sa);
|
return SacrificeAi.doSacOneEachLogic(ai, sa);
|
||||||
|
} else if (sa.getParam("AILogic").equals("Destroy")) {
|
||||||
|
List<Card> tgts = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
|
||||||
|
if (tgts.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Card> alliedTgts = CardLists.filter(tgts, Predicates.or(CardPredicates.isControlledByAnyOf(ai.getAllies()), CardPredicates.isController(ai)));
|
||||||
|
List<Card> oppTgts = CardLists.filter(tgts, CardPredicates.isControlledByAnyOf(ai.getRegisteredOpponents()));
|
||||||
|
|
||||||
|
Card destroyTgt = null;
|
||||||
|
if (!oppTgts.isEmpty()) {
|
||||||
|
destroyTgt = ComputerUtilCard.getBestAI(oppTgts);
|
||||||
|
} else {
|
||||||
|
// TODO: somehow limit this so that the AI doesn't always destroy own stuff when able?
|
||||||
|
destroyTgt = ComputerUtilCard.getWorstAI(alliedTgts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destroyTgt != null) {
|
||||||
|
sa.getTargets().add(destroyTgt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFight) {
|
if (isFight) {
|
||||||
return FightAi.canFightAi(ai, sa, attack, defense);
|
return FightAi.canFightAi(ai, sa, attack, defense);
|
||||||
}
|
}
|
||||||
@@ -517,8 +540,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
|
|
||||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), ai, source, sa);
|
list = CardLists.getValidCards(list, tgt.getValidTgts(), ai, source, sa);
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
// If the cost is tapping, don't activate before declare
|
// If the cost is tapping, don't activate before declare attack/block
|
||||||
// attack/block
|
|
||||||
if (sa.getPayCosts().hasTapCost()) {
|
if (sa.getPayCosts().hasTapCost()) {
|
||||||
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|
||||||
&& game.getPhaseHandler().isPlayerTurn(ai)) {
|
&& game.getPhaseHandler().isPlayerTurn(ai)) {
|
||||||
|
|||||||
@@ -61,20 +61,20 @@ public class PumpAllAi extends PumpAiBase {
|
|||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final Player opp = ai.getStrongestOpponent();
|
final Player opp = ai.getStrongestOpponent();
|
||||||
|
|
||||||
if (tgt != null && sa.canTarget(opp) && sa.hasParam("IsCurse")) {
|
if (tgt != null && sa.canTarget(opp) && sa.isCurse()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tgt != null && sa.canTarget(ai) && !sa.hasParam("IsCurse")) {
|
if (tgt != null && sa.canTarget(ai) && !sa.isCurse()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int power = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumAtt"), sa);
|
final int power = AbilityUtils.calculateAmount(source, sa.getParam("NumAtt"), sa);
|
||||||
final int defense = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumDef"), sa);
|
final int defense = AbilityUtils.calculateAmount(source, sa.getParam("NumDef"), sa);
|
||||||
final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<>();
|
final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<>();
|
||||||
final PhaseType phase = game.getPhaseHandler().getPhase();
|
final PhaseType phase = game.getPhaseHandler().getPhase();
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ public class PumpAllAi extends PumpAiBase {
|
|||||||
if (!game.getStack().isEmpty() && !sa.isCurse()) {
|
if (!game.getStack().isEmpty() && !sa.isCurse()) {
|
||||||
return pumpAgainstRemoval(ai, sa, comp);
|
return pumpAgainstRemoval(ai, sa, comp);
|
||||||
}
|
}
|
||||||
if (sa.hasParam("IsCurse")) {
|
if (sa.isCurse()) {
|
||||||
if (defense < 0) { // try to destroy creatures
|
if (defense < 0) { // try to destroy creatures
|
||||||
comp = CardLists.filter(comp, new Predicate<Card>() {
|
comp = CardLists.filter(comp, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ public class SacrificeAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
|
|
||||||
return sacrificeTgtAI(ai, sa);
|
return sacrificeTgtAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import forge.ai.AiAttackController;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
@@ -8,16 +9,33 @@ import forge.game.spellability.SpellAbility;
|
|||||||
public class SkipPhaseAi extends SpellAbilityAi {
|
public class SkipPhaseAi extends SpellAbilityAi {
|
||||||
@Override
|
@Override
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
return true;
|
return targetPlayer(aiPlayer, sa, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
return mandatory || canPlayAI(aiPlayer, sa);
|
return targetPlayer(aiPlayer, sa, mandatory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean targetPlayer(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
if (sa.usesTargeting()) {
|
||||||
|
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
|
||||||
|
sa.resetTargets();
|
||||||
|
if (sa.canTarget(opp)) {
|
||||||
|
sa.getTargets().add(opp);
|
||||||
|
}
|
||||||
|
else if (mandatory && sa.canTarget(ai)) {
|
||||||
|
sa.getTargets().add(ai);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
|
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
// Planeswalker-related flags
|
// Planeswalker-related flags
|
||||||
boolean pwMinus = false;
|
boolean pwMinus = false;
|
||||||
@@ -143,7 +142,7 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
final Player opp = ai.getWeakestOpponent();
|
final Player opp = ai.getWeakestOpponent();
|
||||||
|
|
||||||
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
if (ComputerUtil.preventRunAwayActivations(sa)) {
|
||||||
return false; // prevent infinite tokens?
|
return false; // prevent infinite tokens?
|
||||||
}
|
}
|
||||||
Card actualToken = spawnToken(ai, sa);
|
Card actualToken = spawnToken(ai, sa);
|
||||||
|
|
||||||
|
|||||||
75
forge-ai/src/main/java/forge/ai/ability/VentureAi.java
Normal file
75
forge-ai/src/main/java/forge/ai/ability/VentureAi.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import forge.ai.AiPlayDecision;
|
||||||
|
import forge.ai.AiProps;
|
||||||
|
import forge.ai.PlayerControllerAi;
|
||||||
|
import forge.ai.SpellAbilityAi;
|
||||||
|
import forge.card.ICardFace;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.util.Aggregates;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class VentureAi extends SpellAbilityAi {
|
||||||
|
@Override
|
||||||
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
|
// TODO: is it ever a bad idea to venture into a dungeon?
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
||||||
|
return mandatory || canPlayAI(aiPlayer, sa);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI that handles choosing the next room in a dungeon
|
||||||
|
@Override
|
||||||
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells, Map<String, Object> params) {
|
||||||
|
List<SpellAbility> viableRooms = Lists.newArrayList();
|
||||||
|
|
||||||
|
for (SpellAbility room : spells) {
|
||||||
|
if (player.getController().isAI()) { // FIXME: is this needed? Can simulation ever run this for a non-AI player?
|
||||||
|
if (((PlayerControllerAi)player.getController()).getAi().canPlaySa(room) == AiPlayDecision.WillPlay) {
|
||||||
|
viableRooms.add(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!viableRooms.isEmpty()) {
|
||||||
|
// choose a room at random from the ones that are deemed playable
|
||||||
|
return Aggregates.random(viableRooms);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Aggregates.random(spells); // If we're here, we should choose at least something, so choose a random thing then
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI that chooses which dungeon to venture into
|
||||||
|
@Override
|
||||||
|
public String chooseCardName(Player ai, SpellAbility sa, List<ICardFace> faces) {
|
||||||
|
// TODO: improve the conditions that define which dungeon is a viable option to choose
|
||||||
|
List<String> dungeonNames = Lists.newArrayList();
|
||||||
|
for (ICardFace face : faces) {
|
||||||
|
dungeonNames.add(face.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't choose Tomb of Annihilation when life in danger unless we can win right away or can't lose for 0 life
|
||||||
|
if (ai.getController().isAI()) { // FIXME: is this needed? Can simulation ever run this for a non-AI player?
|
||||||
|
int lifeInDanger = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD));
|
||||||
|
if ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife())
|
||||||
|
&& !(ai.getLife() > 1 && ai.getWeakestOpponent().getLife() == 1)) {
|
||||||
|
dungeonNames.remove("Tomb of Annihilation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Aggregates.random(dungeonNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.40-SNAPSHOT</version>
|
<version>1.6.43-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-core</artifactId>
|
<artifactId>forge-core</artifactId>
|
||||||
|
|||||||
@@ -167,6 +167,22 @@ public class StaticData {
|
|||||||
return sortedEditions;
|
return sortedEditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TreeMap<CardEdition.Type, List<CardEdition>> editionsTypeMap;
|
||||||
|
public final Map<CardEdition.Type, List<CardEdition>> getEditionsTypeMap(){
|
||||||
|
if (editionsTypeMap == null){
|
||||||
|
editionsTypeMap = new TreeMap<>();
|
||||||
|
for (CardEdition.Type editionType : CardEdition.Type.values()){
|
||||||
|
editionsTypeMap.put(editionType, new ArrayList<>());
|
||||||
|
}
|
||||||
|
for (CardEdition edition : this.getSortedEditions()){
|
||||||
|
CardEdition.Type key = edition.getType();
|
||||||
|
List<CardEdition> editionsOfType = editionsTypeMap.get(key);
|
||||||
|
editionsOfType.add(edition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return editionsTypeMap;
|
||||||
|
}
|
||||||
|
|
||||||
public CardEdition getCardEdition(String setCode){
|
public CardEdition getCardEdition(String setCode){
|
||||||
CardEdition edition = this.editions.get(setCode);
|
CardEdition edition = this.editions.get(setCode);
|
||||||
if (edition == null) // try custom editions
|
if (edition == null) // try custom editions
|
||||||
@@ -284,7 +300,7 @@ public class StaticData {
|
|||||||
return CardDb.CardArtPreference.getPreferences();
|
return CardDb.CardArtPreference.getPreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void setStandardPredicate(Predicate<PaperCard> standardPredicate) { this.standardPredicate = standardPredicate; }
|
public void setStandardPredicate(Predicate<PaperCard> standardPredicate) { this.standardPredicate = standardPredicate; }
|
||||||
|
|
||||||
|
|||||||
@@ -716,7 +716,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return edition != null && edition.getType() != Type.PROMOS;
|
return edition != null && edition.getType() != Type.PROMO;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -728,7 +728,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
|||||||
CardEdition edition = null;
|
CardEdition edition = null;
|
||||||
try {
|
try {
|
||||||
edition = editions.getEditionByCodeOrThrow(paperCard.getEdition());
|
edition = editions.getEditionByCodeOrThrow(paperCard.getEdition());
|
||||||
if (edition.getType() == Type.PROMOS || edition.getType() == Type.REPRINT)
|
if (edition.getType() == Type.PROMO||edition.getType() == Type.REPRINT)
|
||||||
return false;
|
return false;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -56,19 +56,23 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
|
|
||||||
CORE,
|
CORE,
|
||||||
EXPANSION,
|
EXPANSION,
|
||||||
|
|
||||||
REPRINT,
|
|
||||||
ONLINE,
|
|
||||||
STARTER,
|
STARTER,
|
||||||
|
REPRINT,
|
||||||
|
BOXED_SET,
|
||||||
|
|
||||||
DUEL_DECKS,
|
COLLECTOR_EDITION,
|
||||||
PREMIUM_DECK_SERIES,
|
DUEL_DECK,
|
||||||
FROM_THE_VAULT,
|
PROMO,
|
||||||
|
ONLINE,
|
||||||
|
|
||||||
OTHER,
|
DRAFT,
|
||||||
PROMOS,
|
|
||||||
|
COMMANDER,
|
||||||
|
MULTIPLAYER,
|
||||||
FUNNY,
|
FUNNY,
|
||||||
THIRDPARTY; // custom sets
|
|
||||||
|
OTHER, // FALLBACK CATEGORY
|
||||||
|
CUSTOM_SET; // custom sets
|
||||||
|
|
||||||
public String getBoosterBoxDefault() {
|
public String getBoosterBoxDefault() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
@@ -79,6 +83,19 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
return "0";
|
return "0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString(){
|
||||||
|
String[] names = TextUtil.splitWithParenthesis(this.name().toLowerCase(), '_');
|
||||||
|
for (int i = 0; i < names.length; i++)
|
||||||
|
names[i] = TextUtil.capitalize(names[i]);
|
||||||
|
return TextUtil.join(Arrays.asList(names), " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type fromString(String label){
|
||||||
|
List<String> names = Arrays.asList(TextUtil.splitWithParenthesis(label.toUpperCase(), ' '));
|
||||||
|
String value = TextUtil.join(names, "_");
|
||||||
|
return Type.valueOf(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum FoilType {
|
public enum FoilType {
|
||||||
@@ -103,6 +120,7 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
SPECIAL_SLOT("special slot"), //to help with convoluted boosters
|
SPECIAL_SLOT("special slot"), //to help with convoluted boosters
|
||||||
PRECON_PRODUCT("precon product"),
|
PRECON_PRODUCT("precon product"),
|
||||||
BORDERLESS("borderless"),
|
BORDERLESS("borderless"),
|
||||||
|
ETCHED("etched"),
|
||||||
SHOWCASE("showcase"),
|
SHOWCASE("showcase"),
|
||||||
EXTENDED_ART("extended art"),
|
EXTENDED_ART("extended art"),
|
||||||
ALTERNATE_ART("alternate art"),
|
ALTERNATE_ART("alternate art"),
|
||||||
@@ -247,6 +265,7 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
|
|
||||||
private int boosterArts = 1;
|
private int boosterArts = 1;
|
||||||
private SealedProduct.Template boosterTpl = null;
|
private SealedProduct.Template boosterTpl = null;
|
||||||
|
private final Map<String, SealedProduct.Template> boosterTemplates = new HashMap<>();
|
||||||
|
|
||||||
private CardEdition(ListMultimap<String, CardInSet> cardMap, Map<String, Integer> tokens, Map<String, List<String>> customPrintSheetsToParse) {
|
private CardEdition(ListMultimap<String, CardInSet> cardMap, Map<String, Integer> tokens, Map<String, List<String>> customPrintSheetsToParse) {
|
||||||
this.cardMap = cardMap;
|
this.cardMap = cardMap;
|
||||||
@@ -393,11 +412,28 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SealedProduct.Template getBoosterTemplate() {
|
public SealedProduct.Template getBoosterTemplate() {
|
||||||
return boosterTpl;
|
return getBoosterTemplate("Draft");
|
||||||
|
}
|
||||||
|
public SealedProduct.Template getBoosterTemplate(String boosterType) {
|
||||||
|
return boosterTemplates.get(boosterType);
|
||||||
|
}
|
||||||
|
public String getRandomBoosterKind() {
|
||||||
|
List<String> boosterTypes = Lists.newArrayList(boosterTemplates.keySet());
|
||||||
|
|
||||||
|
if (boosterTypes.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.shuffle(boosterTypes);
|
||||||
|
return boosterTypes.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getAvailableBoosterTypes() {
|
||||||
|
return boosterTemplates.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasBoosterTemplate() {
|
public boolean hasBoosterTemplate() {
|
||||||
return boosterTpl != null;
|
return boosterTemplates.containsKey("Draft");
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PrintSheet> getPrintSheetsBySection() {
|
public List<PrintSheet> getPrintSheetsBySection() {
|
||||||
@@ -435,8 +471,16 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Reader extends StorageReaderFolder<CardEdition> {
|
public static class Reader extends StorageReaderFolder<CardEdition> {
|
||||||
|
private boolean isCustomEditions;
|
||||||
|
|
||||||
public Reader(File path) {
|
public Reader(File path) {
|
||||||
super(path, CardEdition.FN_GET_CODE);
|
super(path, CardEdition.FN_GET_CODE);
|
||||||
|
this.isCustomEditions = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reader(File path, boolean isCustomEditions) {
|
||||||
|
super(path, CardEdition.FN_GET_CODE);
|
||||||
|
this.isCustomEditions = isCustomEditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -537,19 +581,38 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
|
|
||||||
res.boosterArts = section.getInt("BoosterCovers", 1);
|
res.boosterArts = section.getInt("BoosterCovers", 1);
|
||||||
String boosterDesc = section.get("Booster");
|
String boosterDesc = section.get("Booster");
|
||||||
res.boosterTpl = boosterDesc == null ? null : new SealedProduct.Template(res.code, SealedProduct.Template.Reader.parseSlots(boosterDesc));
|
|
||||||
|
if (section.contains("Booster")) {
|
||||||
|
// Historical naming convention in Forge for "DraftBooster"
|
||||||
|
res.boosterTpl = new SealedProduct.Template(res.code, SealedProduct.Template.Reader.parseSlots(boosterDesc));
|
||||||
|
res.boosterTemplates.put("Draft", res.boosterTpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] boostertype = { "Draft", "Collector", "Set" };
|
||||||
|
// Theme boosters aren't here because they are closer to preconstructed decks, and should be treated as such
|
||||||
|
for (String type : boostertype) {
|
||||||
|
String name = type + "Booster";
|
||||||
|
if (section.contains(name)) {
|
||||||
|
res.boosterTemplates.put(type, new SealedProduct.Template(res.code, SealedProduct.Template.Reader.parseSlots(section.get(name))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res.alias = section.get("alias");
|
res.alias = section.get("alias");
|
||||||
res.borderColor = BorderColor.valueOf(section.get("border", "Black").toUpperCase(Locale.ENGLISH));
|
res.borderColor = BorderColor.valueOf(section.get("border", "Black").toUpperCase(Locale.ENGLISH));
|
||||||
String type = section.get("type");
|
|
||||||
Type enumType = Type.UNKNOWN;
|
Type enumType = Type.UNKNOWN;
|
||||||
if (null != type && !type.isEmpty()) {
|
if (this.isCustomEditions){
|
||||||
try {
|
enumType = Type.CUSTOM_SET; // Forcing ThirdParty Edition Type to avoid inconsistencies
|
||||||
enumType = Type.valueOf(type.toUpperCase(Locale.ENGLISH));
|
} else {
|
||||||
} catch (IllegalArgumentException ignored) {
|
String type = section.get("type");
|
||||||
// ignore; type will get UNKNOWN
|
if (null != type && !type.isEmpty()) {
|
||||||
System.err.println("Ignoring unknown type in set definitions: name: " + res.name + "; type: " + type);
|
try {
|
||||||
|
enumType = Type.valueOf(type.toUpperCase(Locale.ENGLISH));
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
// ignore; type will get UNKNOWN
|
||||||
|
System.err.println("Ignoring unknown type in set definitions: name: " + res.name + "; type: " + type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
res.type = enumType;
|
res.type = enumType;
|
||||||
res.prerelease = section.get("Prerelease", null);
|
res.prerelease = section.get("Prerelease", null);
|
||||||
@@ -684,14 +747,16 @@ public final class CardEdition implements Comparable<CardEdition> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public IItemReader<SealedProduct.Template> getBoosterGenerator() {
|
public IItemReader<SealedProduct.Template> getBoosterGenerator() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return new StorageReaderBase<SealedProduct.Template>(null) {
|
return new StorageReaderBase<SealedProduct.Template>(null) {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, SealedProduct.Template> readAll() {
|
public Map<String, SealedProduct.Template> readAll() {
|
||||||
Map<String, SealedProduct.Template> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
Map<String, SealedProduct.Template> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||||
for(CardEdition ce : Collection.this) {
|
for(CardEdition ce : Collection.this) {
|
||||||
if (ce.hasBoosterTemplate()) {
|
List<String> boosterTypes = Lists.newArrayList(ce.getAvailableBoosterTypes());
|
||||||
map.put(ce.getCode(), ce.getBoosterTemplate());
|
for (String type : boosterTypes) {
|
||||||
|
String setAffix = type.equals("Draft") ? "" : type;
|
||||||
|
|
||||||
|
map.put(ce.getCode() + setAffix, ce.getBoosterTemplate(type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -34,7 +34,7 @@ import static org.apache.commons.lang3.StringUtils.containsIgnoreCase;
|
|||||||
/**
|
/**
|
||||||
* A collection of methods containing full
|
* A collection of methods containing full
|
||||||
* meta and gameplay properties of a card.
|
* meta and gameplay properties of a card.
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $
|
* @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $
|
||||||
*/
|
*/
|
||||||
@@ -116,7 +116,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
public boolean isVariant() {
|
public boolean isVariant() {
|
||||||
CardType t = getType();
|
CardType t = getType();
|
||||||
return t.isVanguard() || t.isScheme() || t.isPlane() || t.isPhenomenon() || t.isConspiracy();
|
return t.isVanguard() || t.isScheme() || t.isPlane() || t.isPhenomenon() || t.isConspiracy() || t.isDungeon();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CardSplitType getSplitType() {
|
public CardSplitType getSplitType() {
|
||||||
@@ -211,11 +211,20 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean canBeCommander() {
|
public boolean canBeCommander() {
|
||||||
CardType type = mainPart.getType();
|
if (mainPart.getOracleText().contains("can be your commander")) {
|
||||||
if (type.isLegendary() && type.isCreature()) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return mainPart.getOracleText().contains("can be your commander");
|
CardType type = mainPart.getType();
|
||||||
|
boolean creature = type.isCreature();
|
||||||
|
for (String staticAbility : mainPart.getStaticAbilities()) { // Check for Grist
|
||||||
|
if (staticAbility.contains("CharacteristicDefining$ True") && staticAbility.contains("AddType$ Creature")) {
|
||||||
|
creature = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type.isLegendary() && creature) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canBePartnerCommander() {
|
public boolean canBePartnerCommander() {
|
||||||
@@ -234,12 +243,38 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
public boolean canBeBrawlCommander() {
|
public boolean canBeBrawlCommander() {
|
||||||
CardType type = mainPart.getType();
|
CardType type = mainPart.getType();
|
||||||
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
|
if (!type.isLegendary()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (type.isCreature() || type.isPlaneswalker()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grist is checked above, but new cards might work this way
|
||||||
|
for (String staticAbility : mainPart.getStaticAbilities()) {
|
||||||
|
if (staticAbility.contains("CharacteristicDefining$ True") && staticAbility.contains("AddType$ Creature")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canBeTinyLeadersCommander() {
|
public boolean canBeTinyLeadersCommander() {
|
||||||
CardType type = mainPart.getType();
|
CardType type = mainPart.getType();
|
||||||
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
|
if (!type.isLegendary()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (type.isCreature() || type.isPlaneswalker()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grist is checked above, but new cards might work this way
|
||||||
|
for (String staticAbility : mainPart.getStaticAbilities()) {
|
||||||
|
if (staticAbility.contains("CharacteristicDefining$ True") && staticAbility.contains("AddType$ Creature")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMeldWith() {
|
public String getMeldWith() {
|
||||||
@@ -334,7 +369,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the card.
|
* Gets the card.
|
||||||
*
|
*
|
||||||
* @return the card
|
* @return the card
|
||||||
*/
|
*/
|
||||||
public final CardRules getCard() {
|
public final CardRules getCard() {
|
||||||
@@ -370,7 +405,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the line.
|
* Parses the line.
|
||||||
*
|
*
|
||||||
* @param line
|
* @param line
|
||||||
* the line
|
* the line
|
||||||
*/
|
*/
|
||||||
@@ -528,7 +563,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see java.util.Iterator#hasNext()
|
* @see java.util.Iterator#hasNext()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@@ -538,7 +573,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see java.util.Iterator#next()
|
* @see java.util.Iterator#next()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@@ -554,7 +589,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
* @see java.util.Iterator#remove()
|
* @see java.util.Iterator#remove()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -598,6 +598,7 @@ public final class CardRulesPredicates {
|
|||||||
public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardType.CoreType.Scheme);
|
public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardType.CoreType.Scheme);
|
||||||
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
|
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
|
||||||
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
|
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
|
||||||
|
public static final Predicate<CardRules> IS_DUNGEON = CardRulesPredicates.coreType(true, CardType.CoreType.Dungeon);
|
||||||
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
|
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
|
||||||
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
|
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
|
||||||
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
Artifact(true, "artifacts"),
|
Artifact(true, "artifacts"),
|
||||||
Conspiracy(false, "conspiracies"),
|
Conspiracy(false, "conspiracies"),
|
||||||
Creature(true, "creatures"),
|
Creature(true, "creatures"),
|
||||||
|
Dungeon(false, "dungeons"),
|
||||||
Emblem(false, "emblems"),
|
Emblem(false, "emblems"),
|
||||||
Enchantment(true, "enchantments"),
|
Enchantment(true, "enchantments"),
|
||||||
Instant(false, "instants"),
|
Instant(false, "instants"),
|
||||||
@@ -446,6 +447,11 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
return coreTypes.contains(CoreType.Tribal);
|
return coreTypes.contains(CoreType.Tribal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDungeon() {
|
||||||
|
return coreTypes.contains(CoreType.Dungeon);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (calculatedType == null) {
|
if (calculatedType == null) {
|
||||||
@@ -686,13 +692,11 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isMultiwordType(final String type) {
|
private static boolean isMultiwordType(final String type) {
|
||||||
final String[] multiWordTypes = { "Serra's Realm", "Bolas's Meditation Realm" };
|
final String[] multiWordTypes = { "Serra's Realm", "Bolas's Meditation Realm", "Dungeon Master" };
|
||||||
// no need to loop for only 2 exceptions!
|
for (int i = 0; i < multiWordTypes.length; ++i) {
|
||||||
if (multiWordTypes[0].startsWith(type) && !multiWordTypes[0].equals(type)) {
|
if (multiWordTypes[i].startsWith(type) && !multiWordTypes[i].equals(type)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (multiWordTypes[1].startsWith(type) && !multiWordTypes[1].equals(type)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,5 +44,6 @@ public interface CardTypeView extends Iterable<String>, Serializable {
|
|||||||
boolean isPhenomenon();
|
boolean isPhenomenon();
|
||||||
boolean isEmblem();
|
boolean isEmblem();
|
||||||
boolean isTribal();
|
boolean isTribal();
|
||||||
|
boolean isDungeon();
|
||||||
CardTypeView getTypeWithChanges(Iterable<CardChangedType> changedCardTypes);
|
CardTypeView getTypeWithChanges(Iterable<CardChangedType> changedCardTypes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,9 +35,12 @@ public class BoosterPack extends SealedProduct {
|
|||||||
|
|
||||||
public static final Function<CardEdition, BoosterPack> FN_FROM_SET = new Function<CardEdition, BoosterPack>() {
|
public static final Function<CardEdition, BoosterPack> FN_FROM_SET = new Function<CardEdition, BoosterPack>() {
|
||||||
@Override
|
@Override
|
||||||
public BoosterPack apply(final CardEdition arg1) {
|
public BoosterPack apply(final CardEdition edition) {
|
||||||
Template d = StaticData.instance().getBoosters().get(arg1.getCode());
|
String boosterKind = edition.getRandomBoosterKind();
|
||||||
return new BoosterPack(arg1.getName(), d);
|
Template d = edition.getBoosterTemplate(boosterKind);
|
||||||
|
StringBuilder sb = new StringBuilder(edition.getName());
|
||||||
|
sb.append(" ").append(boosterKind);
|
||||||
|
return new BoosterPack(sb.toString(), d);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ public abstract class SealedProduct implements InventoryItemFromSet {
|
|||||||
public static class Template {
|
public static class Template {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public final static Template genericBooster = new Template(null, Lists.newArrayList(
|
public final static Template genericDraftBooster = new Template(null, Lists.newArrayList(
|
||||||
Pair.of(BoosterSlots.COMMON, 10), Pair.of(BoosterSlots.UNCOMMON, 3),
|
Pair.of(BoosterSlots.COMMON, 10), Pair.of(BoosterSlots.UNCOMMON, 3),
|
||||||
Pair.of(BoosterSlots.RARE_MYTHIC, 1), Pair.of(BoosterSlots.BASIC_LAND, 1)
|
Pair.of(BoosterSlots.RARE_MYTHIC, 1), Pair.of(BoosterSlots.BASIC_LAND, 1)
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -236,6 +236,11 @@ public class BoosterGenerator {
|
|||||||
String slotType = slot.getLeft(); // add expansion symbol here?
|
String slotType = slot.getLeft(); // add expansion symbol here?
|
||||||
int numCards = slot.getRight();
|
int numCards = slot.getRight();
|
||||||
|
|
||||||
|
boolean convertCardFoil = slotType.endsWith("+");
|
||||||
|
if (convertCardFoil) {
|
||||||
|
slotType = slotType.substring(0, slotType.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
String[] sType = TextUtil.splitWithParenthesis(slotType, ' ');
|
String[] sType = TextUtil.splitWithParenthesis(slotType, ' ');
|
||||||
String setCode = sType.length == 1 && template.getEdition() != null ? template.getEdition() : null;
|
String setCode = sType.length == 1 && template.getEdition() != null ? template.getEdition() : null;
|
||||||
String sheetKey = StaticData.instance().getEditions().contains(setCode) ? slotType.trim() + " " + setCode
|
String sheetKey = StaticData.instance().getEditions().contains(setCode) ? slotType.trim() + " " + setCode
|
||||||
@@ -280,7 +285,19 @@ public class BoosterGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PrintSheet ps = getPrintSheet(sheetKey);
|
PrintSheet ps = getPrintSheet(sheetKey);
|
||||||
result.addAll(ps.random(numCards, true));
|
List<PaperCard> paperCards;
|
||||||
|
|
||||||
|
// For cards that end in '+', attempt to convert this card to foil.
|
||||||
|
if (convertCardFoil) {
|
||||||
|
paperCards = Lists.newArrayList();
|
||||||
|
for(PaperCard pc : ps.random(numCards, true)) {
|
||||||
|
paperCards.add(pc.getFoiled());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
paperCards = ps.random(numCards, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.addAll(paperCards);
|
||||||
sheetsUsed.add(ps);
|
sheetsUsed.add(ps);
|
||||||
|
|
||||||
if (foilInThisSlot) {
|
if (foilInThisSlot) {
|
||||||
@@ -508,6 +525,7 @@ public class BoosterGenerator {
|
|||||||
mainCode.regionMatches(true, 0, "wholeSheet", 0, 10)
|
mainCode.regionMatches(true, 0, "wholeSheet", 0, 10)
|
||||||
) { // custom print sheet
|
) { // custom print sheet
|
||||||
String sheetName = StringUtils.strip(mainCode.substring(10), "()\" ");
|
String sheetName = StringUtils.strip(mainCode.substring(10), "()\" ");
|
||||||
|
System.out.println("Attempting to lookup :" + sheetName);
|
||||||
src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
|
src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
|
||||||
setPred = Predicates.alwaysTrue();
|
setPred = Predicates.alwaysTrue();
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class TokenDb implements ITokenDatabase {
|
|||||||
|
|
||||||
// colors_power_toughness_cardtypes_sub_types_keywords
|
// colors_power_toughness_cardtypes_sub_types_keywords
|
||||||
// Some examples:
|
// Some examples:
|
||||||
// c_3_3_a_wurm_lifelink
|
// c_3_3_a_phyrexian_wurm_lifelink
|
||||||
// w_2_2_knight_first_strike
|
// w_2_2_knight_first_strike
|
||||||
|
|
||||||
// The image names should be the same as the script name + _set
|
// The image names should be the same as the script name + _set
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.40-SNAPSHOT</version>
|
<version>1.6.43-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-game</artifactId>
|
<artifactId>forge-game</artifactId>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import forge.game.mana.ManaCostBeingPaid;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Expressions;
|
import forge.util.Expressions;
|
||||||
|
|
||||||
public class ForgeScript {
|
public class ForgeScript {
|
||||||
@@ -122,10 +123,8 @@ public class ForgeScript {
|
|||||||
return Expressions.compare(y, property, x);
|
return Expressions.compare(y, property, x);
|
||||||
} else return cardState.getTypeWithChanges().hasStringType(property);
|
} else return cardState.getTypeWithChanges().hasStringType(property);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static boolean spellAbilityHasProperty(SpellAbility sa, String property, Player sourceController,
|
public static boolean spellAbilityHasProperty(SpellAbility sa, String property, Player sourceController,
|
||||||
Card source, CardTraitBase spellAbility) {
|
Card source, CardTraitBase spellAbility) {
|
||||||
if (property.equals("ManaAbility")) {
|
if (property.equals("ManaAbility")) {
|
||||||
@@ -155,6 +154,8 @@ public class ForgeScript {
|
|||||||
return sa.isAftermath();
|
return sa.isAftermath();
|
||||||
} else if (property.equals("MorphUp")) {
|
} else if (property.equals("MorphUp")) {
|
||||||
return sa.isMorphUp();
|
return sa.isMorphUp();
|
||||||
|
} else if (property.equals("Modular")) {
|
||||||
|
return sa.hasParam("Modular");
|
||||||
} else if (property.equals("Equip")) {
|
} else if (property.equals("Equip")) {
|
||||||
return sa.hasParam("Equip");
|
return sa.hasParam("Equip");
|
||||||
} else if (property.equals("Boast")) {
|
} else if (property.equals("Boast")) {
|
||||||
@@ -186,7 +187,14 @@ public class ForgeScript {
|
|||||||
} 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")) {
|
} else if (property.startsWith("cmc")) {
|
||||||
int y = sa.getPayCosts().getTotalMana().getCMC();
|
int y = 0;
|
||||||
|
// spell was on the stack
|
||||||
|
if (sa.getCardState().getCard().isInZone(ZoneType.Stack)) {
|
||||||
|
y = sa.getHostCard().getCMC();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
y = sa.getPayCosts().getTotalMana().getCMC();
|
||||||
|
}
|
||||||
int x = AbilityUtils.calculateAmount(spellAbility.getHostCard(), property.substring(5), spellAbility);
|
int x = AbilityUtils.calculateAmount(spellAbility.getHostCard(), property.substring(5), spellAbility);
|
||||||
if (!Expressions.compare(y, property, x)) {
|
if (!Expressions.compare(y, property, x)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -197,6 +197,15 @@ public class Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CardCollectionView copyLastStateBattlefield() {
|
||||||
|
CardCollection result = new CardCollection();
|
||||||
|
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||||
|
for (final Player p : getPlayers()) {
|
||||||
|
result.addAll(p.getZone(ZoneType.Battlefield).getLKICopy(cachedMap));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public void updateLastStateForCard(Card c) {
|
public void updateLastStateForCard(Card c) {
|
||||||
if (c == null || c.getZone() == null) {
|
if (c == null || c.getZone() == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ public class GameAction {
|
|||||||
copied.setChangedCardTraits(c.getChangedCardTraits());
|
copied.setChangedCardTraits(c.getChangedCardTraits());
|
||||||
|
|
||||||
copied.copyChangedTextFrom(c);
|
copied.copyChangedTextFrom(c);
|
||||||
|
copied.setTimestamp(c.getTimestamp());
|
||||||
|
|
||||||
// copy exiled properties when adding to stack
|
// copy exiled properties when adding to stack
|
||||||
// will be cleanup later in MagicStack
|
// will be cleanup later in MagicStack
|
||||||
@@ -672,6 +673,23 @@ public class GameAction {
|
|||||||
|
|
||||||
c = changeZone(zoneFrom, zoneTo, c, position, cause, params);
|
c = changeZone(zoneFrom, zoneTo, c, position, cause, params);
|
||||||
|
|
||||||
|
// need to refresh ability text for affected cards
|
||||||
|
for (final StaticAbility stAb : c.getStaticAbilities()) {
|
||||||
|
if (stAb.isSecondary() ||
|
||||||
|
!stAb.getParam("Mode").equals("CantBlockBy") ||
|
||||||
|
stAb.isSuppressed() || !stAb.checkConditions() ||
|
||||||
|
!stAb.hasParam("ValidAttacker") ||
|
||||||
|
(stAb.hasParam("ValidBlocker") && stAb.getParam("ValidBlocker").equals("Creature.Self"))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Card host = stAb.getHostCard();
|
||||||
|
for (Card creature : Iterables.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES)) {
|
||||||
|
if (creature.isValid(stAb.getParam("ValidAttacker").split(","), host.getController(), host, stAb)) {
|
||||||
|
creature.updateAbilityTextForView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Move card in maingame if take card from subgame
|
// Move card in maingame if take card from subgame
|
||||||
// 720.4a
|
// 720.4a
|
||||||
if (zoneFrom != null && zoneFrom.is(ZoneType.Sideboard) && game.getMaingame() != null) {
|
if (zoneFrom != null && zoneFrom.is(ZoneType.Sideboard) && game.getMaingame() != null) {
|
||||||
@@ -1175,6 +1193,8 @@ public class GameAction {
|
|||||||
for (final Card c : cards) {
|
for (final Card c : cards) {
|
||||||
// If a token is in a zone other than the battlefield, it ceases to exist.
|
// If a token is in a zone other than the battlefield, it ceases to exist.
|
||||||
checkAgain |= stateBasedAction704_5d(c);
|
checkAgain |= stateBasedAction704_5d(c);
|
||||||
|
// Dungeon Card won't affect other cards, so don't need to set checkAgain
|
||||||
|
stateBasedAction_Dungeon(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1235,7 +1255,7 @@ public class GameAction {
|
|||||||
int loyal = c.getCounters(CounterEnumType.LOYALTY);
|
int loyal = c.getCounters(CounterEnumType.LOYALTY);
|
||||||
if (loyal < beeble) {
|
if (loyal < beeble) {
|
||||||
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
||||||
c.addCounter(CounterEnumType.LOYALTY, beeble - loyal, c.getController(), false, counterTable);
|
c.addCounter(CounterEnumType.LOYALTY, beeble - loyal, c.getController(), null, false, counterTable);
|
||||||
counterTable.triggerCountersPutAll(game);
|
counterTable.triggerCountersPutAll(game);
|
||||||
} else if (loyal > beeble) {
|
} else if (loyal > beeble) {
|
||||||
c.subtractCounter(CounterEnumType.LOYALTY, loyal - beeble);
|
c.subtractCounter(CounterEnumType.LOYALTY, loyal - beeble);
|
||||||
@@ -1261,7 +1281,7 @@ public class GameAction {
|
|||||||
orderedNoRegCreats = true;
|
orderedNoRegCreats = true;
|
||||||
}
|
}
|
||||||
for (Card c : noRegCreats) {
|
for (Card c : noRegCreats) {
|
||||||
sacrificeDestroy(c, null, table);
|
sacrificeDestroy(c, null, table, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (desCreats != null) {
|
if (desCreats != null) {
|
||||||
@@ -1273,7 +1293,7 @@ public class GameAction {
|
|||||||
orderedDesCreats = true;
|
orderedDesCreats = true;
|
||||||
}
|
}
|
||||||
for (Card c : desCreats) {
|
for (Card c : desCreats) {
|
||||||
destroy(c, null, true, table);
|
destroy(c, null, true, table, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setHoldCheckingStaticAbilities(false);
|
setHoldCheckingStaticAbilities(false);
|
||||||
@@ -1359,12 +1379,21 @@ public class GameAction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!game.getStack().hasSourceOnStack(c, SpellAbilityPredicates.isChapter())) {
|
if (!game.getStack().hasSourceOnStack(c, SpellAbilityPredicates.isChapter())) {
|
||||||
sacrifice(c, null, table);
|
sacrifice(c, null, table, null);
|
||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
}
|
}
|
||||||
return checkAgain;
|
return checkAgain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void stateBasedAction_Dungeon(Card c) {
|
||||||
|
if (!c.getType().isDungeon() || !c.isInLastRoom()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!game.getStack().hasSourceOnStack(c, null)) {
|
||||||
|
completeDungeon(c.getController(), c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean stateBasedAction704_attach(Card c, CardZoneTable table) {
|
private boolean stateBasedAction704_attach(Card c, CardZoneTable table) {
|
||||||
boolean checkAgain = false;
|
boolean checkAgain = false;
|
||||||
|
|
||||||
@@ -1387,7 +1416,7 @@ public class GameAction {
|
|||||||
|
|
||||||
// cleanup aura
|
// cleanup aura
|
||||||
if (c.isAura() && c.isInPlay() && !c.isEnchanting()) {
|
if (c.isAura() && c.isInPlay() && !c.isEnchanting()) {
|
||||||
sacrificeDestroy(c, null, table);
|
sacrificeDestroy(c, null, table, null);
|
||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
}
|
}
|
||||||
return checkAgain;
|
return checkAgain;
|
||||||
@@ -1539,7 +1568,7 @@ public class GameAction {
|
|||||||
|
|
||||||
for (Card c : list) {
|
for (Card c : list) {
|
||||||
if (c.getCounters(CounterEnumType.LOYALTY) <= 0) {
|
if (c.getCounters(CounterEnumType.LOYALTY) <= 0) {
|
||||||
sacrificeDestroy(c, null, table);
|
sacrificeDestroy(c, null, table, null);
|
||||||
// Play the Destroy sound
|
// Play the Destroy sound
|
||||||
game.fireEvent(new GameEventCardDestroyed());
|
game.fireEvent(new GameEventCardDestroyed());
|
||||||
recheck = true;
|
recheck = true;
|
||||||
@@ -1602,7 +1631,7 @@ public class GameAction {
|
|||||||
"You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)", null);
|
"You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)", null);
|
||||||
for (Card c: cc) {
|
for (Card c: cc) {
|
||||||
if (c != toKeep) {
|
if (c != toKeep) {
|
||||||
sacrificeDestroy(c, null, table);
|
sacrificeDestroy(c, null, table, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.fireEvent(new GameEventCardDestroyed());
|
game.fireEvent(new GameEventCardDestroyed());
|
||||||
@@ -1636,28 +1665,24 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (Card c : worlds) {
|
for (Card c : worlds) {
|
||||||
sacrificeDestroy(c, null, table);
|
sacrificeDestroy(c, null, table, null);
|
||||||
game.fireEvent(new GameEventCardDestroyed());
|
game.fireEvent(new GameEventCardDestroyed());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
public final Card sacrifice(final Card c, final SpellAbility source, CardZoneTable table, Map<AbilityKey, Object> params) {
|
||||||
public final Card sacrifice(final Card c, final SpellAbility source) {
|
|
||||||
return sacrifice(c, source, null);
|
|
||||||
}
|
|
||||||
public final Card sacrifice(final Card c, final SpellAbility source, CardZoneTable table) {
|
|
||||||
if (!c.canBeSacrificedBy(source)) {
|
if (!c.canBeSacrificedBy(source)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
c.getController().addSacrificedThisTurn(c, source);
|
c.getController().addSacrificedThisTurn(c, source);
|
||||||
|
|
||||||
return sacrificeDestroy(c, source, table);
|
return sacrificeDestroy(c, source, table, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, CardZoneTable table) {
|
public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, CardZoneTable table, Map<AbilityKey, Object> params) {
|
||||||
Player activator = null;
|
Player activator = null;
|
||||||
if (!c.canBeDestroyed()) {
|
if (!c.canBeDestroyed()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1668,6 +1693,9 @@ public class GameAction {
|
|||||||
repRunParams.put(AbilityKey.Source, sa);
|
repRunParams.put(AbilityKey.Source, sa);
|
||||||
repRunParams.put(AbilityKey.Affected, c);
|
repRunParams.put(AbilityKey.Affected, c);
|
||||||
repRunParams.put(AbilityKey.Regeneration, regenerate);
|
repRunParams.put(AbilityKey.Regeneration, regenerate);
|
||||||
|
if (params != null) {
|
||||||
|
repRunParams.putAll(params);
|
||||||
|
}
|
||||||
|
|
||||||
if (game.getReplacementHandler().run(ReplacementType.Destroy, repRunParams) != ReplacementResult.NotReplaced) {
|
if (game.getReplacementHandler().run(ReplacementType.Destroy, repRunParams) != ReplacementResult.NotReplaced) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1684,11 +1712,14 @@ public class GameAction {
|
|||||||
// Run triggers
|
// Run triggers
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
|
||||||
runParams.put(AbilityKey.Causer, activator);
|
runParams.put(AbilityKey.Causer, activator);
|
||||||
|
if (params != null) {
|
||||||
|
runParams.putAll(params);
|
||||||
|
}
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Destroyed, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.Destroyed, runParams, false);
|
||||||
// in case the destroyed card has such a trigger
|
// in case the destroyed card has such a trigger
|
||||||
game.getTriggerHandler().registerActiveLTBTrigger(c);
|
game.getTriggerHandler().registerActiveLTBTrigger(c);
|
||||||
|
|
||||||
final Card sacrificed = sacrificeDestroy(c, sa, table);
|
final Card sacrificed = sacrificeDestroy(c, sa, table, params);
|
||||||
return sacrificed != null;
|
return sacrificed != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1696,12 +1727,12 @@ public class GameAction {
|
|||||||
* @return the sacrificed Card in its new location, or {@code null} if the
|
* @return the sacrificed Card in its new location, or {@code null} if the
|
||||||
* sacrifice wasn't successful.
|
* sacrifice wasn't successful.
|
||||||
*/
|
*/
|
||||||
protected final Card sacrificeDestroy(final Card c, SpellAbility cause, CardZoneTable table) {
|
protected final Card sacrificeDestroy(final Card c, SpellAbility cause, CardZoneTable table, Map<AbilityKey, Object> params) {
|
||||||
if (!c.isInPlay()) {
|
if (!c.isInPlay()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card newCard = moveToGraveyard(c, cause, null);
|
final Card newCard = moveToGraveyard(c, cause, params);
|
||||||
if (table != null && newCard != null && newCard.getZone() != null) {
|
if (table != null && newCard != null && newCard.getZone() != null) {
|
||||||
table.put(ZoneType.Battlefield, newCard.getZone().getZoneType(), newCard);
|
table.put(ZoneType.Battlefield, newCard.getZone().getZoneType(), newCard);
|
||||||
}
|
}
|
||||||
@@ -2168,4 +2199,14 @@ public class GameAction {
|
|||||||
counterTable.triggerCountersPutAll(game);
|
counterTable.triggerCountersPutAll(game);
|
||||||
counterTable.clear();
|
counterTable.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void completeDungeon(Player player, Card dungeon) {
|
||||||
|
player.addCompletedDungeon(dungeon);
|
||||||
|
ceaseToExist(dungeon, true);
|
||||||
|
|
||||||
|
// Run RoomEntered trigger
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(dungeon);
|
||||||
|
runParams.put(AbilityKey.Player, player);
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.DungeonCompleted, runParams, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -638,6 +638,9 @@ public final class GameActionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static CardCollectionView orderCardsByTheirOwners(Game game, CardCollectionView list, ZoneType dest, SpellAbility sa) {
|
public static CardCollectionView orderCardsByTheirOwners(Game game, CardCollectionView list, ZoneType dest, SpellAbility sa) {
|
||||||
|
if (list.size() <= 1) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
CardCollection completeList = new CardCollection();
|
CardCollection completeList = new CardCollection();
|
||||||
for (Player p : game.getPlayers()) {
|
for (Player p : game.getPlayers()) {
|
||||||
CardCollection subList = new CardCollection();
|
CardCollection subList = new CardCollection();
|
||||||
|
|||||||
@@ -205,7 +205,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
public boolean canBeAttached(final Card attach) {
|
public boolean canBeAttached(final Card attach) {
|
||||||
return canBeAttached(attach, false);
|
return canBeAttached(attach, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canBeAttached(final Card attach, boolean checkSBA) {
|
public boolean canBeAttached(final Card attach, boolean checkSBA) {
|
||||||
// master mode
|
// master mode
|
||||||
if (!attach.isAttachment() || attach.isCreature() || equals(attach)) {
|
if (!attach.isAttachment() || attach.isCreature() || equals(attach)) {
|
||||||
@@ -250,7 +249,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean canBeEnchantedBy(final Card aura) {
|
protected boolean canBeEnchantedBy(final Card aura) {
|
||||||
|
|
||||||
SpellAbility sa = aura.getFirstAttachSpell();
|
SpellAbility sa = aura.getFirstAttachSpell();
|
||||||
TargetRestrictions tgt = null;
|
TargetRestrictions tgt = null;
|
||||||
if (sa != null) {
|
if (sa != null) {
|
||||||
@@ -263,7 +261,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
public boolean hasProtectionFrom(final Card source) {
|
public boolean hasProtectionFrom(final Card source) {
|
||||||
return hasProtectionFrom(source, false);
|
return hasProtectionFrom(source, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean hasProtectionFrom(final Card source, final boolean checkSBA);
|
public abstract boolean hasProtectionFrom(final Card source, final boolean checkSBA);
|
||||||
|
|
||||||
// Counters!
|
// Counters!
|
||||||
@@ -280,7 +277,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
Integer value = counters.get(counterName);
|
Integer value = counters.get(counterName);
|
||||||
return value == null ? 0 : value;
|
return value == null ? 0 : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getCounters(final CounterEnumType counterType) {
|
public final int getCounters(final CounterEnumType counterType) {
|
||||||
return getCounters(CounterType.get(counterType));
|
return getCounters(CounterType.get(counterType));
|
||||||
}
|
}
|
||||||
@@ -292,7 +288,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
counters.put(counterType, num);
|
counters.put(counterType, num);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCounters(final CounterEnumType counterType, final Integer num) {
|
public void setCounters(final CounterEnumType counterType, final Integer num) {
|
||||||
setCounters(CounterType.get(counterType), num);
|
setCounters(CounterType.get(counterType), num);
|
||||||
}
|
}
|
||||||
@@ -300,7 +295,7 @@ 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 canReceiveCounters(final CounterType type);
|
abstract public boolean canReceiveCounters(final CounterType type);
|
||||||
abstract public int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table);
|
abstract public int addCounter(final CounterType counterType, final int n, final Player source, final SpellAbility cause, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table);
|
||||||
abstract public void subtractCounter(final CounterType counterName, final int n);
|
abstract public void subtractCounter(final CounterType counterName, final int n);
|
||||||
abstract public void clearCounters();
|
abstract public void clearCounters();
|
||||||
|
|
||||||
@@ -308,8 +303,8 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
return canReceiveCounters(CounterType.get(type));
|
return canReceiveCounters(CounterType.get(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int addCounter(final CounterEnumType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table) {
|
public int addCounter(final CounterEnumType counterType, final int n, final Player source, final SpellAbility cause, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table) {
|
||||||
return addCounter(CounterType.get(counterType), n, source, applyMultiplier, fireEvents, table);
|
return addCounter(CounterType.get(counterType), n, source, cause, applyMultiplier, fireEvents, table);
|
||||||
}
|
}
|
||||||
public void subtractCounter(final CounterEnumType counterName, final int n) {
|
public void subtractCounter(final CounterEnumType counterName, final int n) {
|
||||||
subtractCounter(CounterType.get(counterName), n);
|
subtractCounter(CounterType.get(counterName), n);
|
||||||
|
|||||||
@@ -57,8 +57,28 @@ import forge.util.storage.StorageReaderRecursiveFolderWithUserFolder;
|
|||||||
|
|
||||||
public class GameFormat implements Comparable<GameFormat> {
|
public class GameFormat implements Comparable<GameFormat> {
|
||||||
private final String name;
|
private final String name;
|
||||||
public enum FormatType {Sanctioned, Casual, Historic, Digital, Custom}
|
public enum FormatType {
|
||||||
public enum FormatSubType {Block, Standard, Extended, Pioneer, Modern, Legacy, Vintage, Commander, Planechase, Videogame, MTGO, Arena, Custom}
|
SANCTIONED,
|
||||||
|
CASUAL,
|
||||||
|
HISTORIC,
|
||||||
|
DIGITAL,
|
||||||
|
CUSTOM
|
||||||
|
}
|
||||||
|
public enum FormatSubType {
|
||||||
|
BLOCK,
|
||||||
|
STANDARD,
|
||||||
|
EXTENDED,
|
||||||
|
PIONEER,
|
||||||
|
MODERN,
|
||||||
|
LEGACY,
|
||||||
|
VINTAGE,
|
||||||
|
COMMANDER,
|
||||||
|
PLANECHASE,
|
||||||
|
VIDEOGAME,
|
||||||
|
MTGO,
|
||||||
|
ARENA,
|
||||||
|
CUSTOM
|
||||||
|
}
|
||||||
|
|
||||||
// contains allowed sets, when empty allows all sets
|
// contains allowed sets, when empty allows all sets
|
||||||
private FormatType formatType;
|
private FormatType formatType;
|
||||||
@@ -85,11 +105,11 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
private final int index;
|
private final int index;
|
||||||
|
|
||||||
public GameFormat(final String fName, final Iterable<String> sets, final List<String> bannedCards) {
|
public GameFormat(final String fName, final Iterable<String> sets, final List<String> bannedCards) {
|
||||||
this(fName, parseDate(DEFAULTDATE), sets, bannedCards, null, false, null, null, 0, FormatType.Custom, FormatSubType.Custom);
|
this(fName, parseDate(DEFAULTDATE), sets, bannedCards, null, false, null, null, 0, FormatType.CUSTOM, FormatSubType.CUSTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final GameFormat NoFormat = new GameFormat("(none)", parseDate(DEFAULTDATE) , null, null, null, false
|
public static final GameFormat NoFormat = new GameFormat("(none)", parseDate(DEFAULTDATE) , null, null, null, false
|
||||||
, null, null, Integer.MAX_VALUE, FormatType.Custom, FormatSubType.Custom);
|
, null, null, Integer.MAX_VALUE, FormatType.CUSTOM, FormatSubType.CUSTOM);
|
||||||
|
|
||||||
public GameFormat(final String fName, final Date effectiveDate, final Iterable<String> sets, final List<String> bannedCards,
|
public GameFormat(final String fName, final Date effectiveDate, final Iterable<String> sets, final List<String> bannedCards,
|
||||||
final List<String> restrictedCards, Boolean restrictedLegendary, final List<String> additionalCards,
|
final List<String> restrictedCards, Boolean restrictedLegendary, final List<String> additionalCards,
|
||||||
@@ -276,6 +296,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
if (null == other) {
|
if (null == other) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (other.formatType != formatType){
|
if (other.formatType != formatType){
|
||||||
return formatType.compareTo(other.formatType);
|
return formatType.compareTo(other.formatType);
|
||||||
}else{
|
}else{
|
||||||
@@ -283,10 +304,11 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
return formatSubType.compareTo(other.formatSubType);
|
return formatSubType.compareTo(other.formatSubType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (formatType.equals(FormatType.Historic)){
|
if (formatType.equals(FormatType.HISTORIC)){
|
||||||
if(effectiveDate!=other.effectiveDate) {//for matching dates or default dates default to name sorting
|
int compareDates = this.effectiveDate.compareTo(other.effectiveDate);
|
||||||
return other.effectiveDate.compareTo(effectiveDate);
|
if (compareDates != 0)
|
||||||
}
|
return compareDates;
|
||||||
|
return (this.index - other.index);
|
||||||
}
|
}
|
||||||
return name.compareTo(other.name);
|
return name.compareTo(other.name);
|
||||||
//return index - other.index;
|
//return index - other.index;
|
||||||
@@ -338,15 +360,15 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
String title = section.get("name");
|
String title = section.get("name");
|
||||||
FormatType formatType;
|
FormatType formatType;
|
||||||
try {
|
try {
|
||||||
formatType = FormatType.valueOf(section.get("type"));
|
formatType = FormatType.valueOf(section.get("type").toUpperCase());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
formatType = FormatType.Custom;
|
formatType = FormatType.CUSTOM;
|
||||||
}
|
}
|
||||||
FormatSubType formatsubType;
|
FormatSubType formatsubType;
|
||||||
try {
|
try {
|
||||||
formatsubType = FormatSubType.valueOf(section.get("subtype"));
|
formatsubType = FormatSubType.valueOf(section.get("subtype").toUpperCase());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
formatsubType = FormatSubType.Custom;
|
formatsubType = FormatSubType.CUSTOM;
|
||||||
}
|
}
|
||||||
Integer idx = section.getInt("order");
|
Integer idx = section.getInt("order");
|
||||||
String dateStr = section.get("effective");
|
String dateStr = section.get("effective");
|
||||||
@@ -432,7 +454,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
public Iterable<GameFormat> getSanctionedList() {
|
public Iterable<GameFormat> getSanctionedList() {
|
||||||
List<GameFormat> coreList = new ArrayList<>();
|
List<GameFormat> coreList = new ArrayList<>();
|
||||||
for(GameFormat format: naturallyOrdered){
|
for(GameFormat format: naturallyOrdered){
|
||||||
if(format.getFormatType().equals(FormatType.Sanctioned)){
|
if(format.getFormatType().equals(FormatType.SANCTIONED)){
|
||||||
coreList.add(format);
|
coreList.add(format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,8 +464,8 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
public Iterable<GameFormat> getFilterList() {
|
public Iterable<GameFormat> getFilterList() {
|
||||||
List<GameFormat> coreList = new ArrayList<>();
|
List<GameFormat> coreList = new ArrayList<>();
|
||||||
for(GameFormat format: naturallyOrdered){
|
for(GameFormat format: naturallyOrdered){
|
||||||
if(!format.getFormatType().equals(FormatType.Historic)
|
if(!format.getFormatType().equals(FormatType.HISTORIC)
|
||||||
&&!format.getFormatType().equals(FormatType.Digital)){
|
&&!format.getFormatType().equals(FormatType.DIGITAL)){
|
||||||
coreList.add(format);
|
coreList.add(format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -453,7 +475,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
public Iterable<GameFormat> getHistoricList() {
|
public Iterable<GameFormat> getHistoricList() {
|
||||||
List<GameFormat> coreList = new ArrayList<>();
|
List<GameFormat> coreList = new ArrayList<>();
|
||||||
for(GameFormat format: naturallyOrdered){
|
for(GameFormat format: naturallyOrdered){
|
||||||
if(format.getFormatType().equals(FormatType.Historic)){
|
if(format.getFormatType().equals(FormatType.HISTORIC)){
|
||||||
coreList.add(format);
|
coreList.add(format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -463,7 +485,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
public Map<String, List<GameFormat>> getHistoricMap() {
|
public Map<String, List<GameFormat>> getHistoricMap() {
|
||||||
Map<String, List<GameFormat>> coreList = new HashMap<>();
|
Map<String, List<GameFormat>> coreList = new HashMap<>();
|
||||||
for(GameFormat format: naturallyOrdered){
|
for(GameFormat format: naturallyOrdered){
|
||||||
if(format.getFormatType().equals(FormatType.Historic)){
|
if(format.getFormatType().equals(FormatType.HISTORIC)){
|
||||||
String alpha = format.getName().substring(0,1);
|
String alpha = format.getName().substring(0,1);
|
||||||
if(!coreList.containsKey(alpha)){
|
if(!coreList.containsKey(alpha)){
|
||||||
coreList.put(alpha,new ArrayList<>());
|
coreList.put(alpha,new ArrayList<>());
|
||||||
@@ -528,15 +550,15 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
Set<FormatSubType> coveredTypes = new HashSet<>();
|
Set<FormatSubType> coveredTypes = new HashSet<>();
|
||||||
CardPool allCards = deck.getAllCardsInASinglePool();
|
CardPool allCards = deck.getAllCardsInASinglePool();
|
||||||
for(GameFormat gf : reverseDateOrdered) {
|
for(GameFormat gf : reverseDateOrdered) {
|
||||||
if (gf.getFormatType().equals(FormatType.Digital) && !exhaustive){
|
if (gf.getFormatType().equals(FormatType.DIGITAL) && !exhaustive){
|
||||||
//exclude Digital formats from lists for now
|
//exclude Digital formats from lists for now
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (gf.getFormatSubType().equals(FormatSubType.Commander)){
|
if (gf.getFormatSubType().equals(FormatSubType.COMMANDER)){
|
||||||
//exclude Commander format as other deck checks are not performed here
|
//exclude Commander format as other deck checks are not performed here
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (gf.getFormatType().equals(FormatType.Historic) && coveredTypes.contains(gf.getFormatSubType())
|
if (gf.getFormatType().equals(FormatType.HISTORIC) && coveredTypes.contains(gf.getFormatSubType())
|
||||||
&& !exhaustive){
|
&& !exhaustive){
|
||||||
//exclude duplicate formats - only keep first of e.g. Standard historical
|
//exclude duplicate formats - only keep first of e.g. Standard historical
|
||||||
continue;
|
continue;
|
||||||
@@ -570,7 +592,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
return gf1.formatSubType.compareTo(gf2.formatSubType);
|
return gf1.formatSubType.compareTo(gf2.formatSubType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gf1.formatType.equals(FormatType.Historic)){
|
if (gf1.formatType.equals(FormatType.HISTORIC)){
|
||||||
if(gf1.effectiveDate!=gf2.effectiveDate) {//for matching dates or default dates default to name sorting
|
if(gf1.effectiveDate!=gf2.effectiveDate) {//for matching dates or default dates default to name sorting
|
||||||
return gf1.effectiveDate.compareTo(gf2.effectiveDate);
|
return gf1.effectiveDate.compareTo(gf2.effectiveDate);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ public class Match {
|
|||||||
|
|
||||||
if (!lostOwnership.isEmpty()) {
|
if (!lostOwnership.isEmpty()) {
|
||||||
List<PaperCard> lostPaperOwnership = new ArrayList<>();
|
List<PaperCard> lostPaperOwnership = new ArrayList<>();
|
||||||
for(Card c : lostOwnership) {
|
for (Card c : lostOwnership) {
|
||||||
lostPaperOwnership.add((PaperCard)c.getPaperCard());
|
lostPaperOwnership.add((PaperCard)c.getPaperCard());
|
||||||
}
|
}
|
||||||
outcome.addAnteLost(registered, lostPaperOwnership);
|
outcome.addAnteLost(registered, lostPaperOwnership);
|
||||||
@@ -357,7 +357,7 @@ public class Match {
|
|||||||
|
|
||||||
if (!gainedOwnership.isEmpty()) {
|
if (!gainedOwnership.isEmpty()) {
|
||||||
List<PaperCard> gainedPaperOwnership = new ArrayList<>();
|
List<PaperCard> gainedPaperOwnership = new ArrayList<>();
|
||||||
for(Card c : gainedOwnership) {
|
for (Card c : gainedOwnership) {
|
||||||
gainedPaperOwnership.add((PaperCard)c.getPaperCard());
|
gainedPaperOwnership.add((PaperCard)c.getPaperCard());
|
||||||
}
|
}
|
||||||
outcome.addAnteWon(registered, gainedPaperOwnership);
|
outcome.addAnteWon(registered, gainedPaperOwnership);
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -28,6 +28,7 @@ import forge.card.CardStateName;
|
|||||||
import forge.game.CardTraitBase;
|
import forge.game.CardTraitBase;
|
||||||
import forge.game.IHasSVars;
|
import forge.game.IHasSVars;
|
||||||
import forge.game.ability.effects.CharmEffect;
|
import forge.game.ability.effects.CharmEffect;
|
||||||
|
import forge.game.ability.effects.RollDiceEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardState;
|
import forge.game.card.CardState;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
@@ -45,7 +46,7 @@ import io.sentry.event.BreadcrumbBuilder;
|
|||||||
* <p>
|
* <p>
|
||||||
* AbilityFactory class.
|
* AbilityFactory class.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
@@ -71,7 +72,7 @@ public final class AbilityFactory {
|
|||||||
Spell("SP"),
|
Spell("SP"),
|
||||||
StaticAbility("ST"),
|
StaticAbility("ST"),
|
||||||
SubAbility("DB");
|
SubAbility("DB");
|
||||||
|
|
||||||
private final String prefix;
|
private final String prefix;
|
||||||
AbilityRecordType(String prefix) {
|
AbilityRecordType(String prefix) {
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
@@ -79,7 +80,7 @@ public final class AbilityFactory {
|
|||||||
public String getPrefix() {
|
public String getPrefix() {
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpellAbility buildSpellAbility(ApiType api, Card hostCard, Cost abCost, TargetRestrictions abTgt, Map<String, String> mapParams ) {
|
public SpellAbility buildSpellAbility(ApiType api, Card hostCard, Cost abCost, TargetRestrictions abTgt, Map<String, String> mapParams ) {
|
||||||
switch(this) {
|
switch(this) {
|
||||||
case Ability: return new AbilityApiBased(api, hostCard, abCost, abTgt, mapParams);
|
case Ability: return new AbilityApiBased(api, hostCard, abCost, abTgt, mapParams);
|
||||||
@@ -89,11 +90,11 @@ public final class AbilityFactory {
|
|||||||
}
|
}
|
||||||
return null; // exception here would be fine!
|
return null; // exception here would be fine!
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApiType getApiTypeOf(Map<String, String> abParams) {
|
public ApiType getApiTypeOf(Map<String, String> abParams) {
|
||||||
return ApiType.smartValueOf(abParams.get(this.getPrefix()));
|
return ApiType.smartValueOf(abParams.get(this.getPrefix()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AbilityRecordType getRecordType(Map<String, String> abParams) {
|
public static AbilityRecordType getRecordType(Map<String, String> abParams) {
|
||||||
if (abParams.containsKey(AbilityRecordType.Ability.getPrefix())) {
|
if (abParams.containsKey(AbilityRecordType.Ability.getPrefix())) {
|
||||||
return AbilityRecordType.Ability;
|
return AbilityRecordType.Ability;
|
||||||
@@ -108,7 +109,7 @@ public final class AbilityFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final SpellAbility getAbility(final String abString, final Card card) {
|
public static final SpellAbility getAbility(final String abString, final Card card) {
|
||||||
return getAbility(abString, card, card.getCurrentState());
|
return getAbility(abString, card, card.getCurrentState());
|
||||||
}
|
}
|
||||||
@@ -119,7 +120,7 @@ public final class AbilityFactory {
|
|||||||
* <p>
|
* <p>
|
||||||
* getAbility.
|
* getAbility.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param abString
|
* @param abString
|
||||||
* a {@link java.lang.String} object.
|
* a {@link java.lang.String} object.
|
||||||
* @param state
|
* @param state
|
||||||
@@ -129,7 +130,7 @@ public final class AbilityFactory {
|
|||||||
public static final SpellAbility getAbility(final String abString, final CardState state) {
|
public static final SpellAbility getAbility(final String abString, final CardState state) {
|
||||||
return getAbility(abString, state, state);
|
return getAbility(abString, state, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final SpellAbility getAbility(final String abString, final CardState state, final IHasSVars sVarHolder) {
|
private static final SpellAbility getAbility(final String abString, final CardState state, final IHasSVars sVarHolder) {
|
||||||
Map<String, String> mapParams;
|
Map<String, String> mapParams;
|
||||||
try {
|
try {
|
||||||
@@ -155,7 +156,7 @@ public final class AbilityFactory {
|
|||||||
throw new RuntimeException(msg + " of card: " + state.getName(), ex);
|
throw new RuntimeException(msg + " of card: " + state.getName(), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final SpellAbility getAbility(final Card hostCard, final String svar) {
|
public static final SpellAbility getAbility(final Card hostCard, final String svar) {
|
||||||
return getAbility(hostCard, svar, hostCard.getCurrentState());
|
return getAbility(hostCard, svar, hostCard.getCurrentState());
|
||||||
}
|
}
|
||||||
@@ -163,7 +164,7 @@ public final class AbilityFactory {
|
|||||||
public static final SpellAbility getAbility(final Card hostCard, final String svar, final IHasSVars sVarHolder) {
|
public static final SpellAbility getAbility(final Card hostCard, final String svar, final IHasSVars sVarHolder) {
|
||||||
return getAbility(hostCard.getCurrentState(), svar, sVarHolder);
|
return getAbility(hostCard.getCurrentState(), svar, sVarHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final SpellAbility getAbility(final CardState state, final String svar, final IHasSVars sVarHolder) {
|
public static final SpellAbility getAbility(final CardState state, final String svar, final IHasSVars sVarHolder) {
|
||||||
if (!sVarHolder.hasSVar(svar)) {
|
if (!sVarHolder.hasSVar(svar)) {
|
||||||
String source = state.getCard().getName();
|
String source = state.getCard().getName();
|
||||||
@@ -285,15 +286,18 @@ public final class AbilityFactory {
|
|||||||
@Override
|
@Override
|
||||||
public AbilitySub apply(String input) {
|
public AbilitySub apply(String input) {
|
||||||
return getSubAbility(state, input, sVarHolder);
|
return getSubAbility(state, input, sVarHolder);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (api == ApiType.RollDice) {
|
if (api == ApiType.RollDice) {
|
||||||
for (String param : mapParams.keySet()) {
|
final String key = "ResultSubAbilities";
|
||||||
if (param.startsWith("On") || param.equals("Else")) {
|
if (mapParams.containsKey(key)) {
|
||||||
spellAbility.setAdditionalAbility(param, getSubAbility(state, mapParams.get(param), sVarHolder));
|
String [] diceAbilities = mapParams.get(key).split(",");
|
||||||
|
for (String ab : diceAbilities) {
|
||||||
|
String [] kv = ab.split(":");
|
||||||
|
spellAbility.setAdditionalAbility(kv[0], getSubAbility(state, kv[1], sVarHolder));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,7 +315,6 @@ public final class AbilityFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sb.append(mapParams.get("SpellDescription"));
|
sb.append(mapParams.get("SpellDescription"));
|
||||||
|
|
||||||
spellAbility.setDescription(sb.toString());
|
spellAbility.setDescription(sb.toString());
|
||||||
} else if (api == ApiType.Charm) {
|
} else if (api == ApiType.Charm) {
|
||||||
spellAbility.setDescription(CharmEffect.makeFormatedDescription(spellAbility));
|
spellAbility.setDescription(CharmEffect.makeFormatedDescription(spellAbility));
|
||||||
@@ -319,6 +322,12 @@ public final class AbilityFactory {
|
|||||||
spellAbility.setDescription("");
|
spellAbility.setDescription("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (api == ApiType.RollDice) {
|
||||||
|
spellAbility.setDescription(spellAbility.getDescription() + RollDiceEffect.makeFormatedDescription(spellAbility));
|
||||||
|
} else if (api == ApiType.Repeat) {
|
||||||
|
spellAbility.setDescription(spellAbility.getDescription() + spellAbility.getAdditionalAbility("RepeatSubAbility").getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
initializeParams(spellAbility);
|
initializeParams(spellAbility);
|
||||||
makeRestrictions(spellAbility);
|
makeRestrictions(spellAbility);
|
||||||
makeConditions(spellAbility);
|
makeConditions(spellAbility);
|
||||||
@@ -399,7 +408,7 @@ public final class AbilityFactory {
|
|||||||
* <p>
|
* <p>
|
||||||
* initializeParams.
|
* initializeParams.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
*/
|
*/
|
||||||
@@ -414,7 +423,7 @@ public final class AbilityFactory {
|
|||||||
* <p>
|
* <p>
|
||||||
* makeRestrictions.
|
* makeRestrictions.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
*/
|
*/
|
||||||
@@ -428,7 +437,7 @@ public final class AbilityFactory {
|
|||||||
* <p>
|
* <p>
|
||||||
* makeConditions.
|
* makeConditions.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param sa
|
* @param sa
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
*/
|
*/
|
||||||
@@ -444,7 +453,7 @@ public final class AbilityFactory {
|
|||||||
* getSubAbility.
|
* getSubAbility.
|
||||||
* </p>
|
* </p>
|
||||||
* @param sSub
|
* @param sSub
|
||||||
*
|
*
|
||||||
* @return a {@link forge.game.spellability.AbilitySub} object.
|
* @return a {@link forge.game.spellability.AbilitySub} object.
|
||||||
*/
|
*/
|
||||||
private static final AbilitySub getSubAbility(CardState state, String sSub, final IHasSVars sVarHolder) {
|
private static final AbilitySub getSubAbility(CardState state, String sSub, final IHasSVars sVarHolder) {
|
||||||
@@ -466,19 +475,19 @@ public final class AbilityFactory {
|
|||||||
List<ZoneType> origin = ZoneType.listValueOf(params.get("Origin"));
|
List<ZoneType> origin = ZoneType.listValueOf(params.get("Origin"));
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
|
|
||||||
// Don't set the zone if it targets a player
|
// Don't set the zone if it targets a player
|
||||||
if ((tgt != null) && !tgt.canTgtPlayer()) {
|
if ((tgt != null) && !tgt.canTgtPlayer()) {
|
||||||
sa.getTargetRestrictions().setZone(origin);
|
sa.getTargetRestrictions().setZone(origin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final SpellAbility buildFusedAbility(final Card card) {
|
public static final SpellAbility buildFusedAbility(final Card card) {
|
||||||
if(!card.isSplitCard())
|
if(!card.isSplitCard())
|
||||||
throw new IllegalStateException("Fuse ability may be built only on split cards");
|
throw new IllegalStateException("Fuse ability may be built only on split cards");
|
||||||
|
|
||||||
CardState leftState = card.getState(CardStateName.LeftSplit);
|
CardState leftState = card.getState(CardStateName.LeftSplit);
|
||||||
SpellAbility leftAbility = leftState.getFirstAbility();
|
SpellAbility leftAbility = leftState.getFirstAbility();
|
||||||
Map<String, String> leftMap = Maps.newHashMap(leftAbility.getMapParams());
|
Map<String, String> leftMap = Maps.newHashMap(leftAbility.getMapParams());
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ public enum AbilityKey {
|
|||||||
IsCombatDamage("IsCombatDamage"),
|
IsCombatDamage("IsCombatDamage"),
|
||||||
IndividualCostPaymentInstance("IndividualCostPaymentInstance"),
|
IndividualCostPaymentInstance("IndividualCostPaymentInstance"),
|
||||||
IsMadness("IsMadness"),
|
IsMadness("IsMadness"),
|
||||||
|
LastStateBattlefield("LastStateBattlefield"),
|
||||||
LifeAmount("LifeAmount"), //TODO confirm that this and LifeGained can be merged
|
LifeAmount("LifeAmount"), //TODO confirm that this and LifeGained can be merged
|
||||||
LifeGained("LifeGained"),
|
LifeGained("LifeGained"),
|
||||||
Mana("Mana"),
|
Mana("Mana"),
|
||||||
@@ -106,6 +107,7 @@ public enum AbilityKey {
|
|||||||
ReplacementResult("ReplacementResult"),
|
ReplacementResult("ReplacementResult"),
|
||||||
ReplacementResultMap("ReplacementResultMap"),
|
ReplacementResultMap("ReplacementResultMap"),
|
||||||
Result("Result"),
|
Result("Result"),
|
||||||
|
RoomName("RoomName"),
|
||||||
Scheme("Scheme"),
|
Scheme("Scheme"),
|
||||||
Source("Source"),
|
Source("Source"),
|
||||||
Sources("Sources"),
|
Sources("Sources"),
|
||||||
|
|||||||
@@ -173,15 +173,13 @@ public class AbilityUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (defined.equals("Targeted") && sa instanceof SpellAbility) {
|
else if (defined.equals("Targeted") && sa instanceof SpellAbility) {
|
||||||
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingCard();
|
for (TargetChoices tc : ((SpellAbility)sa).getAllTargetChoices()) {
|
||||||
if (saTargeting != null) {
|
Iterables.addAll(cards, tc.getTargetCards());
|
||||||
Iterables.addAll(cards, saTargeting.getTargets().getTargetCards());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (defined.equals("TargetedSource") && sa instanceof SpellAbility) {
|
else if (defined.equals("TargetedSource") && sa instanceof SpellAbility) {
|
||||||
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingSA();
|
for (TargetChoices tc : ((SpellAbility)sa).getAllTargetChoices()) {
|
||||||
if (saTargeting != null) {
|
for (SpellAbility s : tc.getTargetSpells()) {
|
||||||
for (SpellAbility s : saTargeting.getTargets().getTargetSpells()) {
|
|
||||||
cards.add(s.getHostCard());
|
cards.add(s.getHostCard());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -742,7 +740,18 @@ public class AbilityUtils {
|
|||||||
final SpellAbility root = sa.getRootAbility();
|
final SpellAbility root = sa.getRootAbility();
|
||||||
final String[] l = calcX[1].split("/");
|
final String[] l = calcX[1].split("/");
|
||||||
final String m = CardFactoryUtil.extractOperators(calcX[1]);
|
final String m = CardFactoryUtil.extractOperators(calcX[1]);
|
||||||
final Integer count = (Integer) root.getTriggeringObject(AbilityKey.fromString(l[0]));
|
Integer count = null;
|
||||||
|
if (calcX[0].endsWith("Max")) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Iterable<Integer> numbers = (Iterable<Integer>) root.getTriggeringObject(AbilityKey.fromString(l[0]));
|
||||||
|
for (Integer n : numbers) {
|
||||||
|
if (count == null || n > count) {
|
||||||
|
count = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count = (Integer) root.getTriggeringObject(AbilityKey.fromString(l[0]));
|
||||||
|
}
|
||||||
|
|
||||||
val = doXMath(ObjectUtils.firstNonNull(count, 0), m, card, ability);
|
val = doXMath(ObjectUtils.firstNonNull(count, 0), m, card, ability);
|
||||||
}
|
}
|
||||||
@@ -807,9 +816,9 @@ public class AbilityUtils {
|
|||||||
final SpellAbility root = sa.getRootAbility();
|
final SpellAbility root = sa.getRootAbility();
|
||||||
list = new CardCollection((Card) root.getReplacingObject(AbilityKey.fromString(calcX[0].substring(8))));
|
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) {
|
if (list != null) {
|
||||||
|
// there could be null inside!
|
||||||
|
list = Iterables.filter(list, Card.class);
|
||||||
val = handlePaid(list, calcX[1], card, ability);
|
val = handlePaid(list, calcX[1], card, ability);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1008,9 +1017,8 @@ public class AbilityUtils {
|
|||||||
players.addAll(getDefinedPlayers(card, "TargetedController", sa));
|
players.addAll(getDefinedPlayers(card, "TargetedController", sa));
|
||||||
}
|
}
|
||||||
else if ((defined.equals("Targeted") || defined.equals("TargetedPlayer")) && sa instanceof SpellAbility) {
|
else if ((defined.equals("Targeted") || defined.equals("TargetedPlayer")) && sa instanceof SpellAbility) {
|
||||||
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingPlayer();
|
for (TargetChoices tc : ((SpellAbility)sa).getAllTargetChoices()) {
|
||||||
if (saTargeting != null) {
|
players.addAll(tc.getTargetPlayers());
|
||||||
players.addAll(saTargeting.getTargets().getTargetPlayers());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (defined.equals("ParentTarget") && sa instanceof SpellAbility) {
|
else if (defined.equals("ParentTarget") && sa instanceof SpellAbility) {
|
||||||
@@ -1289,8 +1297,7 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
SpellAbility s = null;
|
SpellAbility s = null;
|
||||||
|
|
||||||
// TODO - this probably needs to be fleshed out a bit, but the basics
|
// TODO - this probably needs to be fleshed out a bit, but the basics work
|
||||||
// work
|
|
||||||
if (defined.equals("Self") && sa instanceof SpellAbility) {
|
if (defined.equals("Self") && sa instanceof SpellAbility) {
|
||||||
s = (SpellAbility)sa;
|
s = (SpellAbility)sa;
|
||||||
}
|
}
|
||||||
@@ -1298,9 +1305,8 @@ public class AbilityUtils {
|
|||||||
s = ((SpellAbility)sa).getRootAbility();
|
s = ((SpellAbility)sa).getRootAbility();
|
||||||
}
|
}
|
||||||
else if (defined.equals("Targeted") && sa instanceof SpellAbility) {
|
else if (defined.equals("Targeted") && sa instanceof SpellAbility) {
|
||||||
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingSA();
|
for (TargetChoices tc : ((SpellAbility)sa).getAllTargetChoices()) {
|
||||||
if (saTargeting != null) {
|
for (SpellAbility targetSpell : tc.getTargetSpells()) {
|
||||||
for (SpellAbility targetSpell : saTargeting.getTargets().getTargetSpells()) {
|
|
||||||
SpellAbilityStackInstance stackInstance = game.getStack().getInstanceFromSpellAbility(targetSpell);
|
SpellAbilityStackInstance stackInstance = game.getStack().getInstanceFromSpellAbility(targetSpell);
|
||||||
if (stackInstance != null) {
|
if (stackInstance != null) {
|
||||||
SpellAbility instanceSA = stackInstance.getSpellAbility(true);
|
SpellAbility instanceSA = stackInstance.getSpellAbility(true);
|
||||||
@@ -1412,7 +1418,6 @@ public class AbilityUtils {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AbilityUtils.resolveApiAbility(sa, game);
|
AbilityUtils.resolveApiAbility(sa, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2575,7 +2580,6 @@ public class AbilityUtils {
|
|||||||
// Count$ThisTurnCast <Valid>
|
// Count$ThisTurnCast <Valid>
|
||||||
// Count$LastTurnCast <Valid>
|
// Count$LastTurnCast <Valid>
|
||||||
if (sq[0].startsWith("ThisTurnCast") || sq[0].startsWith("LastTurnCast")) {
|
if (sq[0].startsWith("ThisTurnCast") || sq[0].startsWith("LastTurnCast")) {
|
||||||
|
|
||||||
final String[] workingCopy = l[0].split("_");
|
final String[] workingCopy = l[0].split("_");
|
||||||
final String validFilter = workingCopy[1];
|
final String validFilter = workingCopy[1];
|
||||||
|
|
||||||
@@ -2736,7 +2740,6 @@ public class AbilityUtils {
|
|||||||
return doXMath(powers.size(), expr, c, ctb);
|
return doXMath(powers.size(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (sq[0].startsWith("MostProminentCreatureType")) {
|
if (sq[0].startsWith("MostProminentCreatureType")) {
|
||||||
String restriction = l[0].split(" ")[1];
|
String restriction = l[0].split(" ")[1];
|
||||||
CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), restriction, player, c, ctb);
|
CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), restriction, player, c, ctb);
|
||||||
@@ -3366,6 +3369,29 @@ public class AbilityUtils {
|
|||||||
return doXMath(opps == null ? 0 : opps.size(), m, source, ctb);
|
return doXMath(opps == null ? 0 : opps.size(), m, source, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.equals("DungeonsCompleted")) {
|
||||||
|
return doXMath(player.getCompletedDungeons().size(), m, source, ctb);
|
||||||
|
}
|
||||||
|
if (value.equals("DifferentlyNamedDungeonsCompleted")) {
|
||||||
|
int amount = 0;
|
||||||
|
List<Card> dungeons = player.getCompletedDungeons();
|
||||||
|
for (int i = 0; i < dungeons.size(); ++i) {
|
||||||
|
Card d1 = dungeons.get(i);
|
||||||
|
boolean hasSameName = false;
|
||||||
|
for (int j = i - 1; j >= 0; --j) {
|
||||||
|
Card d2 = dungeons.get(j);
|
||||||
|
if (d1.getName().equals(d2.getName())) {
|
||||||
|
hasSameName = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasSameName) {
|
||||||
|
++amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return doXMath(amount, m, source, ctb);
|
||||||
|
}
|
||||||
|
|
||||||
return doXMath(0, m, source, ctb);
|
return doXMath(0, m, source, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3395,7 +3421,6 @@ public class AbilityUtils {
|
|||||||
return doXMath(n, CardFactoryUtil.extractOperators(s), source, ctb);
|
return doXMath(n, CardFactoryUtil.extractOperators(s), source, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* handlePaid.
|
* handlePaid.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import java.util.Map;
|
|||||||
import forge.game.ability.effects.*;
|
import forge.game.ability.effects.*;
|
||||||
import forge.util.ReflectionUtil;
|
import forge.util.ReflectionUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -172,6 +172,7 @@ public enum ApiType {
|
|||||||
UnattachAll (UnattachAllEffect.class),
|
UnattachAll (UnattachAllEffect.class),
|
||||||
Untap (UntapEffect.class),
|
Untap (UntapEffect.class),
|
||||||
UntapAll (UntapAllEffect.class),
|
UntapAll (UntapAllEffect.class),
|
||||||
|
Venture (VentureEffect.class),
|
||||||
Vote (VoteEffect.class),
|
Vote (VoteEffect.class),
|
||||||
WinsGame (GameWinEffect.class),
|
WinsGame (GameWinEffect.class),
|
||||||
|
|
||||||
@@ -187,7 +188,7 @@ public enum ApiType {
|
|||||||
private final Class<? extends SpellAbilityEffect> clsEffect;
|
private final Class<? extends SpellAbilityEffect> clsEffect;
|
||||||
|
|
||||||
private static final Map<String, ApiType> allValues = new HashMap<>();
|
private static final Map<String, ApiType> allValues = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
for(ApiType t : ApiType.values()) {
|
for(ApiType t : ApiType.values()) {
|
||||||
allValues.put(t.name().toLowerCase(), t);
|
allValues.put(t.name().toLowerCase(), t);
|
||||||
@@ -197,7 +198,7 @@ public enum ApiType {
|
|||||||
ApiType(Class<? extends SpellAbilityEffect> clsEf) { this(clsEf, true); }
|
ApiType(Class<? extends SpellAbilityEffect> clsEf) { this(clsEf, true); }
|
||||||
ApiType(Class<? extends SpellAbilityEffect> clsEf, final boolean isStateLess) {
|
ApiType(Class<? extends SpellAbilityEffect> clsEf, final boolean isStateLess) {
|
||||||
clsEffect = clsEf;
|
clsEffect = clsEf;
|
||||||
instanceEffect = isStateLess ? ReflectionUtil.makeDefaultInstanceOf(clsEf) : null;
|
instanceEffect = isStateLess ? ReflectionUtil.makeDefaultInstanceOf(clsEf) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ApiType smartValueOf(String value) {
|
public static ApiType smartValueOf(String value) {
|
||||||
|
|||||||
@@ -329,7 +329,6 @@ public abstract class SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static void addSelfTrigger(final SpellAbility sa, String location, final Card card) {
|
protected static void addSelfTrigger(final SpellAbility sa, String location, final Card card) {
|
||||||
|
|
||||||
String trigStr = "Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield " +
|
String trigStr = "Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield " +
|
||||||
"| TriggerDescription$ At the beginning of the end step, " + location.toLowerCase() + " CARDNAME.";
|
"| TriggerDescription$ At the beginning of the end step, " + location.toLowerCase() + " CARDNAME.";
|
||||||
|
|
||||||
@@ -511,7 +510,7 @@ public abstract class SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// build an Effect with that infomation
|
// build an Effect with that information
|
||||||
String name = host.getName() + "'s Effect";
|
String name = host.getName() + "'s Effect";
|
||||||
|
|
||||||
final Card eff = createEffect(sa, controller, name, host.getImageKey());
|
final Card eff = createEffect(sa, controller, name, host.getImageKey());
|
||||||
@@ -617,8 +616,29 @@ public abstract class SpellAbilityEffect {
|
|||||||
combat.addBlocker(attacker, c);
|
combat.addBlocker(attacker, c);
|
||||||
combat.orderAttackersForDamageAssignment(c);
|
combat.orderAttackersForDamageAssignment(c);
|
||||||
|
|
||||||
|
{
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Attacker, attacker);
|
||||||
|
runParams.put(AbilityKey.Blocker, c);
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlockedByCreature, runParams, false);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Attackers, attacker);
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlockedOnce, runParams, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Run triggers for new blocker and add it to damage assignment order
|
// Run triggers for new blocker and add it to damage assignment order
|
||||||
if (!wasBlocked) {
|
if (!wasBlocked) {
|
||||||
|
final CardCollection blockers = combat.getBlockers(attacker);
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Attacker, attacker);
|
||||||
|
runParams.put(AbilityKey.Blockers, blockers);
|
||||||
|
runParams.put(AbilityKey.NumBlockers, blockers.size());
|
||||||
|
runParams.put(AbilityKey.Defender, combat.getDefenderByAttacker(attacker));
|
||||||
|
runParams.put(AbilityKey.DefendingPlayer, combat.getDefenderPlayerByAttacker(attacker));
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false);
|
||||||
|
|
||||||
combat.setBlocked(attacker, true);
|
combat.setBlocked(attacker, true);
|
||||||
combat.addBlockerToDamageAssignmentOrder(attacker, c);
|
combat.addBlockerToDamageAssignmentOrder(attacker, c);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ public class AmassEffect extends TokenEffectBase {
|
|||||||
|
|
||||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||||
for(final Card tgtCard : tgtCards) {
|
for(final Card tgtCard : tgtCards) {
|
||||||
tgtCard.addCounter(CounterEnumType.P1P1, amount, activator, true, table);
|
tgtCard.addCounter(CounterEnumType.P1P1, amount, activator, sa, true, table);
|
||||||
game.updateLastStateForCard(tgtCard);
|
game.updateLastStateForCard(tgtCard);
|
||||||
|
|
||||||
if (remember) {
|
if (remember) {
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
if (types.hasSubtype("ChosenType")) {
|
if (types.hasSubtype("ChosenType")) {
|
||||||
types.clear();
|
types.clear();
|
||||||
types.add(host.getChosenType());
|
types.add(host.getChosenType());
|
||||||
|
} else if (types.hasSubtype("ChosenType2")) {
|
||||||
|
types.clear();
|
||||||
|
types.add(host.getChosenType2());
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<String> keywords = new ArrayList<>();
|
final List<String> keywords = new ArrayList<>();
|
||||||
@@ -125,7 +128,7 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CardCollectionView list;
|
CardCollectionView list;
|
||||||
|
|
||||||
if (!sa.usesTargeting() && !sa.hasParam("Defined")) {
|
if (!sa.usesTargeting() && !sa.hasParam("Defined")) {
|
||||||
list = game.getCardsIn(ZoneType.Battlefield);
|
list = game.getCardsIn(ZoneType.Battlefield);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
if (types.hasSubtype("ChosenType")) {
|
if (types.hasSubtype("ChosenType")) {
|
||||||
types.clear();
|
types.clear();
|
||||||
types.add(source.getChosenType());
|
types.add(source.getChosenType());
|
||||||
|
} else if (types.hasSubtype("ChosenType2")) {
|
||||||
|
types.clear();
|
||||||
|
types.add(source.getChosenType2());
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<String> keywords = Lists.newArrayList();
|
final List<String> keywords = Lists.newArrayList();
|
||||||
@@ -227,7 +230,7 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
}
|
}
|
||||||
// allow SVar substitution for keywords
|
// allow SVar substitution for keywords
|
||||||
for (int i = 0; i < keywords.size(); i++) {
|
for (int i = 0; i < keywords.size(); i++) {
|
||||||
final String k = keywords.get(i);
|
final String k = keywords.get(i);
|
||||||
if (sa.hasSVar(k)) {
|
if (sa.hasSVar(k)) {
|
||||||
keywords.add("\"" + k + "\"");
|
keywords.add("\"" + k + "\"");
|
||||||
keywords.remove(k);
|
keywords.remove(k);
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (!addType.isEmpty() || !removeType.isEmpty() || removeCreatureTypes) {
|
if (!addType.isEmpty() || !removeType.isEmpty() || removeCreatureTypes) {
|
||||||
c.addChangedCardTypes(addType, removeType, removeSuperTypes, removeCardTypes, removeSubTypes,
|
c.addChangedCardTypes(addType, removeType, removeSuperTypes, removeCardTypes, removeSubTypes,
|
||||||
removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes, timestamp);
|
removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes, timestamp, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.addChangedCardKeywords(keywords, removeKeywords, removeAll, removeLandTypes, timestamp);
|
c.addChangedCardKeywords(keywords, removeKeywords, removeAll, removeLandTypes, timestamp);
|
||||||
@@ -133,7 +133,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
c.addHiddenExtrinsicKeyword(k);
|
c.addHiddenExtrinsicKeyword(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.addColor(colors, !sa.hasParam("OverwriteColors"), timestamp);
|
c.addColor(colors, !sa.hasParam("OverwriteColors"), timestamp, false);
|
||||||
|
|
||||||
if (sa.hasParam("LeaveBattlefield")) {
|
if (sa.hasParam("LeaveBattlefield")) {
|
||||||
addLeaveBattlefieldReplacement(c, sa, sa.getParam("LeaveBattlefield"));
|
addLeaveBattlefieldReplacement(c, sa, sa.getParam("LeaveBattlefield"));
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class AttachEffect extends SpellAbilityEffect {
|
|||||||
// If Cast Targets will be checked on the Stack
|
// If Cast Targets will be checked on the Stack
|
||||||
for (final Card attachment : attachments) {
|
for (final Card attachment : attachments) {
|
||||||
String message = Localizer.getInstance().getMessage("lblDoYouWantAttachSourceToTarget", CardTranslation.getTranslatedName(attachment.getName()), attachToName);
|
String message = Localizer.getInstance().getMessage("lblDoYouWantAttachSourceToTarget", CardTranslation.getTranslatedName(attachment.getName()), attachToName);
|
||||||
if ( sa.hasParam("Optional") && !p.getController().confirmAction(sa, null, message) )
|
if (sa.hasParam("Optional") && !p.getController().confirmAction(sa, null, message))
|
||||||
continue;
|
continue;
|
||||||
handleAttachment(attachment, attachTo, sa);
|
handleAttachment(attachment, attachTo, sa);
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,6 @@ public class AttachEffect extends SpellAbilityEffect {
|
|||||||
* the o
|
* the o
|
||||||
*/
|
*/
|
||||||
public static void handleAttachment(final Card card, final Object o, final SpellAbility sa) {
|
public static void handleAttachment(final Card card, final Object o, final SpellAbility sa) {
|
||||||
|
|
||||||
if (card == null) { return; }
|
if (card == null) { return; }
|
||||||
|
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import java.util.Map;
|
|||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
@@ -46,7 +47,8 @@ public class BalanceEffect extends SpellAbilityEffect {
|
|||||||
validCards.add(CardLists.getValidCards(players.get(i).getCardsIn(zone), valid, activator, source, sa));
|
validCards.add(CardLists.getValidCards(players.get(i).getCardsIn(zone), valid, activator, source, sa));
|
||||||
min = Math.min(min, validCards.get(i).size());
|
min = Math.min(min, validCards.get(i).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
CardZoneTable table = new CardZoneTable();
|
CardZoneTable table = new CardZoneTable();
|
||||||
for (int i = 0; i < players.size(); i++) {
|
for (int i = 0; i < players.size(); i++) {
|
||||||
Player p = players.get(i);
|
Player p = players.get(i);
|
||||||
@@ -59,7 +61,7 @@ public class BalanceEffect extends SpellAbilityEffect {
|
|||||||
} else { // Battlefield
|
} else { // Battlefield
|
||||||
for (Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) {
|
for (Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) {
|
||||||
if ( null == card ) continue;
|
if ( null == card ) continue;
|
||||||
game.getAction().sacrifice(card, sa, table);
|
game.getAction().sacrifice(card, sa, table, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,7 +291,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
* @return a {@link java.lang.String} object.
|
* @return a {@link java.lang.String} object.
|
||||||
*/
|
*/
|
||||||
private static String changeKnownOriginStackDescription(final SpellAbility sa) {
|
private static String changeKnownOriginStackDescription(final SpellAbility sa) {
|
||||||
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
|
||||||
@@ -709,7 +708,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("WithCountersType")) {
|
if (sa.hasParam("WithCountersType")) {
|
||||||
CounterType cType = CounterType.getType(sa.getParam("WithCountersType"));
|
CounterType cType = CounterType.getType(sa.getParam("WithCountersType"));
|
||||||
int cAmount = AbilityUtils.calculateAmount(hostCard, sa.getParamOrDefault("WithCountersAmount", "1"), sa);
|
int cAmount = AbilityUtils.calculateAmount(hostCard, sa.getParamOrDefault("WithCountersAmount", "1"), sa);
|
||||||
movedCard.addCounter(cType, cAmount, player, true, counterTable);
|
movedCard.addCounter(cType, cAmount, player, sa, true, counterTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("ExileFaceDown") || sa.hasParam("FaceDown")) {
|
if (sa.hasParam("ExileFaceDown") || sa.hasParam("FaceDown")) {
|
||||||
@@ -1047,7 +1046,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
// only multi-select if player can select more than one
|
// only multi-select if player can select more than one
|
||||||
if (changeNum > 1 && allowMultiSelect(decider, sa)) {
|
if (changeNum > 1 && allowMultiSelect(decider, sa)) {
|
||||||
List<Card> selectedCards;
|
List<Card> selectedCards;
|
||||||
if (! sa.hasParam("SelectPrompt")) {
|
if (!sa.hasParam("SelectPrompt")) {
|
||||||
// new default messaging for multi select
|
// new default messaging for multi select
|
||||||
if (fetchList.size() > changeNum) {
|
if (fetchList.size() > changeNum) {
|
||||||
//Select up to %changeNum cards from %players %origin
|
//Select up to %changeNum cards from %players %origin
|
||||||
|
|||||||
@@ -200,7 +200,6 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void chainAbilities(SpellAbility sa, List<AbilitySub> chosen) {
|
private static void chainAbilities(SpellAbility sa, List<AbilitySub> chosen) {
|
||||||
|
|
||||||
if (chosen == null) {
|
if (chosen == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -243,5 +242,4 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public class ChooseTypeEffect extends SpellAbilityEffect {
|
|||||||
for (final Player p : tgtPlayers) {
|
for (final Player p : tgtPlayers) {
|
||||||
if ((tgt == null) || p.canBeTargetedBy(sa)) {
|
if ((tgt == null) || p.canBeTargetedBy(sa)) {
|
||||||
String choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes);
|
String choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes);
|
||||||
if (!sa.hasParam("Secondary")) {
|
if (!sa.hasParam("ChooseType2")) {
|
||||||
card.setChosenType(choice);
|
card.setChosenType(choice);
|
||||||
} else {
|
} else {
|
||||||
card.setChosenType2(choice);
|
card.setChosenType2(choice);
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
|||||||
tgtCard.updateStateForView();
|
tgtCard.updateStateForView();
|
||||||
|
|
||||||
// when clone is itself, cleanup from old abilities
|
// when clone is itself, cleanup from old abilities
|
||||||
if (host.equals(tgtCard)) {
|
if (host.equals(tgtCard) && !sa.hasParam("ImprintRememberedNoCleanup")) {
|
||||||
tgtCard.clearImprintedCards();
|
tgtCard.clearImprintedCards();
|
||||||
tgtCard.clearRemembered();
|
tgtCard.clearRemembered();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import java.util.List;
|
|||||||
|
|
||||||
import forge.GameCommand;
|
import forge.GameCommand;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.Lang;
|
import forge.util.Lang;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Write javadoc for this type.
|
* TODO: Write javadoc for this type.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -18,16 +19,18 @@ public class ControlPlayerEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
|
|
||||||
List<Player> tgtPlayers = getTargetPlayers(sa);
|
List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||||
return TextUtil.concatWithSpace(sa.getActivatingPlayer().toString(),"controls", Lang.joinHomogenous(tgtPlayers),"during their next turn");
|
return TextUtil.concatWithSpace(sa.getActivatingPlayer().toString(),"controls", Lang.joinHomogenous(tgtPlayers),"during their next turn");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Player activator = sa.getActivatingPlayer();
|
final Player activator = sa.getActivatingPlayer();
|
||||||
final Game game = activator.getGame();
|
final Game game = activator.getGame();
|
||||||
|
final Player controller = sa.hasParam("Controller") ? AbilityUtils.getDefinedPlayers(
|
||||||
|
sa.getHostCard(), sa.getParam("Controller"), sa).get(0) : activator;
|
||||||
|
|
||||||
List<Player> tgtPlayers = getTargetPlayers(sa);
|
List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||||
|
|
||||||
@@ -37,13 +40,13 @@ public class ControlPlayerEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// CR 800.4b
|
// CR 800.4b
|
||||||
if (activator.hasLost()) {
|
if (controller.hasLost()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long ts = game.getNextTimestamp();
|
long ts = game.getNextTimestamp();
|
||||||
pTarget.addController(ts, activator);
|
pTarget.addController(ts, controller);
|
||||||
|
|
||||||
// on following cleanup release control
|
// on following cleanup release control
|
||||||
game.getEndOfTurn().addUntil(new GameCommand() {
|
game.getEndOfTurn().addUntil(new GameCommand() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -99,9 +99,9 @@ public class CopyPermanentEffect extends TokenEffectBase {
|
|||||||
if (sa.hasParam("RandomCopied")) {
|
if (sa.hasParam("RandomCopied")) {
|
||||||
List<PaperCard> copysource = Lists.newArrayList(cards);
|
List<PaperCard> copysource = Lists.newArrayList(cards);
|
||||||
List<Card> choice = Lists.newArrayList();
|
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);
|
int ncopied = AbilityUtils.calculateAmount(host, num, sa);
|
||||||
while(ncopied > 0 && !copysource.isEmpty()) {
|
while (ncopied > 0 && !copysource.isEmpty()) {
|
||||||
final PaperCard cp = Aggregates.random(copysource);
|
final PaperCard cp = Aggregates.random(copysource);
|
||||||
Card possibleCard = Card.fromPaperCard(cp, activator); // Need to temporarily set the Owner so the Game is set
|
Card possibleCard = Card.fromPaperCard(cp, activator); // Need to temporarily set the Owner so the Game is set
|
||||||
|
|
||||||
|
|||||||
@@ -73,10 +73,8 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
controllers = AbilityUtils.getDefinedPlayers(card, sa.getParam("Controller"), sa);
|
controllers = AbilityUtils.getDefinedPlayers(card, sa.getParam("Controller"), sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final List<SpellAbility> tgtSpells = getTargetSpells(sa);
|
final List<SpellAbility> tgtSpells = getTargetSpells(sa);
|
||||||
|
|
||||||
|
|
||||||
if (tgtSpells.size() == 0 || amount == 0) {
|
if (tgtSpells.size() == 0 || amount == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import forge.game.ability.AbilityKey;
|
|||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardFactoryUtil;
|
||||||
|
import forge.game.card.CardZoneTable;
|
||||||
import forge.game.replacement.ReplacementResult;
|
import forge.game.replacement.ReplacementResult;
|
||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -113,6 +114,8 @@ public class CounterEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
|
CardZoneTable table = new CardZoneTable();
|
||||||
for (final SpellAbility tgtSA : sas) {
|
for (final SpellAbility tgtSA : sas) {
|
||||||
final Card tgtSACard = tgtSA.getHostCard();
|
final Card tgtSACard = tgtSA.getHostCard();
|
||||||
// should remember even that spell cannot be countered, e.g. Dovescape
|
// should remember even that spell cannot be countered, e.g. Dovescape
|
||||||
@@ -137,7 +140,7 @@ public class CounterEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
// Destroy Permanent may be able to be turned into a SubAbility
|
// Destroy Permanent may be able to be turned into a SubAbility
|
||||||
if (tgtSA.isAbility() && sa.hasParam("DestroyPermanent")) {
|
if (tgtSA.isAbility() && sa.hasParam("DestroyPermanent")) {
|
||||||
game.getAction().destroy(tgtSACard, sa, true, null);
|
game.getAction().destroy(tgtSACard, sa, true, table, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("RememberCountered")) {
|
if (sa.hasParam("RememberCountered")) {
|
||||||
@@ -152,6 +155,7 @@ public class CounterEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
table.triggerChangesZoneAll(game, sa);
|
||||||
} // end counterResolve
|
} // end counterResolve
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,7 +193,7 @@ public class CounterEffect extends SpellAbilityEffect {
|
|||||||
params.put(AbilityKey.StackSi, si);
|
params.put(AbilityKey.StackSi, si);
|
||||||
|
|
||||||
String destination = srcSA.hasParam("Destination") ? srcSA.getParam("Destination") : tgtSA.isAftermath() ? "Exile" : "Graveyard";
|
String destination = srcSA.hasParam("Destination") ? srcSA.getParam("Destination") : tgtSA.isAftermath() ? "Exile" : "Graveyard";
|
||||||
if (srcSA.hasParam("DestinationChoice")) {//Hinder
|
if (srcSA.hasParam("DestinationChoice")) { //Hinder
|
||||||
List<String> pos = Arrays.asList(srcSA.getParam("DestinationChoice").split(","));
|
List<String> pos = Arrays.asList(srcSA.getParam("DestinationChoice").split(","));
|
||||||
destination = srcSA.getActivatingPlayer().getController().chooseSomeType(Localizer.getInstance().getMessage("lblRemoveDestination"), tgtSA, pos, null);
|
destination = srcSA.getActivatingPlayer().getController().chooseSomeType(Localizer.getInstance().getMessage("lblRemoveDestination"), tgtSA, pos, null);
|
||||||
}
|
}
|
||||||
@@ -207,7 +211,6 @@ public class CounterEffect extends SpellAbilityEffect {
|
|||||||
} else if (destination.equals("Battlefield")) {
|
} else if (destination.equals("Battlefield")) {
|
||||||
if (tgtSA instanceof SpellPermanent) {
|
if (tgtSA instanceof SpellPermanent) {
|
||||||
Card c = tgtSA.getHostCard();
|
Card c = tgtSA.getHostCard();
|
||||||
System.out.println(c + " is SpellPermanent");
|
|
||||||
c.setController(srcSA.getActivatingPlayer(), 0);
|
c.setController(srcSA.getActivatingPlayer(), 0);
|
||||||
game.getAction().moveToPlay(c, srcSA.getActivatingPlayer(), srcSA, params);
|
game.getAction().moveToPlay(c, srcSA.getActivatingPlayer(), srcSA, params);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -147,8 +147,7 @@ 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
|
// rule 121.5: If the first and second objects are the same object, nothing happens
|
||||||
// happens
|
|
||||||
if (src.equals(dest)) {
|
if (src.equals(dest)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -163,7 +162,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Map.Entry<CounterType, Integer> e : countersToAdd.entrySet()) {
|
for (Map.Entry<CounterType, Integer> e : countersToAdd.entrySet()) {
|
||||||
dest.addCounter(e.getKey(), e.getValue(), player, true, table);
|
dest.addCounter(e.getKey(), e.getValue(), player, sa, true, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
game.updateLastStateForCard(dest);
|
game.updateLastStateForCard(dest);
|
||||||
@@ -199,8 +198,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
boolean updateSource = false;
|
boolean updateSource = false;
|
||||||
|
|
||||||
for (final Card dest : tgtCards) {
|
for (final Card dest : tgtCards) {
|
||||||
// rule 121.5: If the first and second objects are the same object, nothing
|
// rule 121.5: If the first and second objects are the same object, nothing happens
|
||||||
// happens
|
|
||||||
if (source.equals(dest)) {
|
if (source.equals(dest)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -225,7 +223,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (cnum > 0) {
|
if (cnum > 0) {
|
||||||
source.subtractCounter(cType, cnum);
|
source.subtractCounter(cType, cnum);
|
||||||
cur.addCounter(cType, cnum, player, true, table);
|
cur.addCounter(cType, cnum, player, sa, true, table);
|
||||||
game.updateLastStateForCard(cur);
|
game.updateLastStateForCard(cur);
|
||||||
updateSource = true;
|
updateSource = true;
|
||||||
}
|
}
|
||||||
@@ -262,8 +260,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
for (final Card dest : tgtCards) {
|
for (final Card dest : tgtCards) {
|
||||||
if (null != dest) {
|
if (null != dest) {
|
||||||
// rule 121.5: If the first and second objects are the same object, nothing
|
// rule 121.5: If the first and second objects are the same object, nothing happens
|
||||||
// happens
|
|
||||||
if (source.equals(dest)) {
|
if (source.equals(dest)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -311,7 +308,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (Map.Entry<CounterType, Integer> e : countersToAdd.entrySet()) {
|
for (Map.Entry<CounterType, Integer> e : countersToAdd.entrySet()) {
|
||||||
cur.addCounter(e.getKey(), e.getValue(), player, true, table);
|
cur.addCounter(e.getKey(), e.getValue(), player, sa, true, table);
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(cur);
|
game.updateLastStateForCard(cur);
|
||||||
}
|
}
|
||||||
@@ -329,8 +326,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
final PlayerController pc = player.getController();
|
final PlayerController pc = player.getController();
|
||||||
final Game game = host.getGame();
|
final Game game = host.getGame();
|
||||||
|
|
||||||
// rule 121.5: If the first and second objects are the same object, nothing
|
// rule 121.5: If the first and second objects are the same object, nothing happens
|
||||||
// happens
|
|
||||||
if (src.equals(dest)) {
|
if (src.equals(dest)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,10 +54,10 @@ public class CountersMultiplyEffect extends SpellAbilityEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (counterType != null) {
|
if (counterType != null) {
|
||||||
gameCard.addCounter(counterType, gameCard.getCounters(counterType) * n, player, true, table);
|
gameCard.addCounter(counterType, gameCard.getCounters(counterType) * n, player, sa, true, table);
|
||||||
} else {
|
} else {
|
||||||
for (Map.Entry<CounterType, Integer> e : gameCard.getCounters().entrySet()) {
|
for (Map.Entry<CounterType, Integer> e : gameCard.getCounters().entrySet()) {
|
||||||
gameCard.addCounter(e.getKey(), e.getValue() * n, player, true, table);
|
gameCard.addCounter(e.getKey(), e.getValue() * n, player, sa, true, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(gameCard);
|
game.updateLastStateForCard(gameCard);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class CountersNoteEffect extends SpellAbilityEffect {
|
|||||||
if (mode.equals(MODE_STORE)) {
|
if (mode.equals(MODE_STORE)) {
|
||||||
noteCounters(c, source);
|
noteCounters(c, source);
|
||||||
} else if (mode.equals(MODE_LOAD)) {
|
} else if (mode.equals(MODE_LOAD)) {
|
||||||
loadCounters(c, source, p, table);
|
loadCounters(c, source, p, sa, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
table.triggerCountersPutAll(game);
|
table.triggerCountersPutAll(game);
|
||||||
@@ -44,13 +44,13 @@ public class CountersNoteEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadCounters(Card notee, Card source, final Player p, GameEntityCounterTable table) {
|
private void loadCounters(Card notee, Card source, final Player p, final SpellAbility sa, GameEntityCounterTable table) {
|
||||||
for(Entry<String, String> svar : source.getSVars().entrySet()) {
|
for(Entry<String, String> svar : source.getSVars().entrySet()) {
|
||||||
String key = svar.getKey();
|
String key = svar.getKey();
|
||||||
if (key.startsWith(NOTE_COUNTERS)) {
|
if (key.startsWith(NOTE_COUNTERS)) {
|
||||||
notee.addCounter(
|
notee.addCounter(
|
||||||
CounterType.getType(key.substring(NOTE_COUNTERS.length())),
|
CounterType.getType(key.substring(NOTE_COUNTERS.length())),
|
||||||
Integer.parseInt(svar.getValue()), p, false, table);
|
Integer.parseInt(svar.getValue()), p, sa, false, table);
|
||||||
}
|
}
|
||||||
// TODO Probably should "remove" the svars that were temporarily used
|
// TODO Probably should "remove" the svars that were temporarily used
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
|
|||||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||||
for (final GameEntity ge : result) {
|
for (final GameEntity ge : result) {
|
||||||
for (final CounterType ct : ge.getCounters().keySet()) {
|
for (final CounterType ct : ge.getCounters().keySet()) {
|
||||||
ge.addCounter(ct, 1, p, true, true, table);
|
ge.addCounter(ct, 1, p, sa, true, true, table);
|
||||||
}
|
}
|
||||||
if (ge instanceof Card) {
|
if (ge instanceof Card) {
|
||||||
Card c = (Card) ge;
|
Card c = (Card) ge;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class CountersPutAllEffect extends SpellAbilityEffect {
|
|||||||
if (etbcounter) {
|
if (etbcounter) {
|
||||||
tgtCard.addEtbCounter(CounterType.getType(type), counterAmount, placer);
|
tgtCard.addEtbCounter(CounterType.getType(type), counterAmount, placer);
|
||||||
} else {
|
} else {
|
||||||
tgtCard.addCounter(CounterType.getType(type), counterAmount, placer, inBattlefield, table);
|
tgtCard.addCounter(CounterType.getType(type), counterAmount, placer, sa, inBattlefield, table);
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(tgtCard);
|
game.updateLastStateForCard(tgtCard);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,10 +226,10 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
if (eachExistingCounter) {
|
if (eachExistingCounter) {
|
||||||
for (CounterType ct : choices) {
|
for (CounterType ct : choices) {
|
||||||
if (obj instanceof Player) {
|
if (obj instanceof Player) {
|
||||||
((Player) obj).addCounter(ct, counterAmount, placer, true, table);
|
((Player) obj).addCounter(ct, counterAmount, placer, sa, true, table);
|
||||||
}
|
}
|
||||||
if (obj instanceof Card) {
|
if (obj instanceof Card) {
|
||||||
gameCard.addCounter(ct, counterAmount, placer, true, table);
|
gameCard.addCounter(ct, counterAmount, placer, sa, true, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -253,7 +253,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
for (Card c : AbilityUtils.getDefinedCards(card, sa.getParam("EachFromSource"), sa)) {
|
for (Card c : AbilityUtils.getDefinedCards(card, sa.getParam("EachFromSource"), sa)) {
|
||||||
for (Entry<CounterType, Integer> cti : c.getCounters().entrySet()) {
|
for (Entry<CounterType, Integer> cti : c.getCounters().entrySet()) {
|
||||||
if (gameCard != null && gameCard.canReceiveCounters(cti.getKey())) {
|
if (gameCard != null && gameCard.canReceiveCounters(cti.getKey())) {
|
||||||
gameCard.addCounter(cti.getKey(), cti.getValue(), placer, true, table);
|
gameCard.addCounter(cti.getKey(), cti.getValue(), placer, sa, true, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
if (etbcounter) {
|
if (etbcounter) {
|
||||||
gameCard.addEtbCounter(counterType, counterAmount, placer);
|
gameCard.addEtbCounter(counterType, counterAmount, placer);
|
||||||
} else {
|
} else {
|
||||||
int addedAmount = gameCard.addCounter(counterType, counterAmount, placer, true, table);
|
int addedAmount = gameCard.addCounter(counterType, counterAmount, placer, sa, true, table);
|
||||||
if (addedAmount > 0) {
|
if (addedAmount > 0) {
|
||||||
counterAdded = true;
|
counterAdded = true;
|
||||||
}
|
}
|
||||||
@@ -372,7 +372,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
if (etbcounter) {
|
if (etbcounter) {
|
||||||
gameCard.addEtbCounter(counterType, counterAmount, placer);
|
gameCard.addEtbCounter(counterType, counterAmount, placer);
|
||||||
} else {
|
} else {
|
||||||
if (gameCard.addCounter(counterType, counterAmount, placer, false, table) > 0) {
|
if (gameCard.addCounter(counterType, counterAmount, placer, sa, false, table) > 0) {
|
||||||
counterAdded = true;
|
counterAdded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -388,7 +388,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
} else if (obj instanceof Player) {
|
} else if (obj instanceof Player) {
|
||||||
// Add Counters to players!
|
// Add Counters to players!
|
||||||
Player pl = (Player) obj;
|
Player pl = (Player) obj;
|
||||||
pl.addCounter(counterType, counterAmount, placer, true, table);
|
pl.addCounter(counterType, counterAmount, placer, sa, true, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
boolean apply = zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack);
|
boolean apply = zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack);
|
||||||
|
|
||||||
tgtCard.addCounter(chosenType, counterAmount, pl, apply, table);
|
tgtCard.addCounter(chosenType, counterAmount, pl, sa, apply, table);
|
||||||
} else {
|
} else {
|
||||||
tgtCard.subtractCounter(chosenType, counterAmount);
|
tgtCard.subtractCounter(chosenType, counterAmount);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
// if use targeting we show all targets and corresponding damage
|
// if use targeting we show all targets and corresponding damage
|
||||||
if (spellAbility.usesTargeting()) {
|
if (spellAbility.usesTargeting()) {
|
||||||
if (spellAbility.hasParam("DivideEvenly")) {
|
if (spellAbility.hasParam("DivideEvenly")) {
|
||||||
stringBuilder.append("divided evenly (rounded down) to\n");
|
stringBuilder.append("divided evenly (rounded down) to \n");
|
||||||
} else if (spellAbility.isDividedAsYouChoose()) {
|
} else if (spellAbility.isDividedAsYouChoose()) {
|
||||||
stringBuilder.append("divided to\n");
|
stringBuilder.append("divided to \n");
|
||||||
} else
|
} else
|
||||||
stringBuilder.append("to ");
|
stringBuilder.append("to ");
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ public class DelayedTriggerEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.google.common.collect.Maps;
|
|||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameActionUtil;
|
import forge.game.GameActionUtil;
|
||||||
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -44,7 +45,6 @@ public class DestroyAllEffect extends SpellAbilityEffect {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
|
|
||||||
final boolean noRegen = sa.hasParam("NoRegen");
|
final boolean noRegen = sa.hasParam("NoRegen");
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
final Game game = sa.getActivatingPlayer().getGame();
|
||||||
@@ -90,10 +90,12 @@ public class DestroyAllEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CardZoneTable table = new CardZoneTable();
|
CardZoneTable table = new CardZoneTable();
|
||||||
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
|
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||||
|
|
||||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||||
for (Card c : list) {
|
for (Card c : list) {
|
||||||
if (game.getAction().destroy(c, sa, !noRegen, table) && remDestroyed) {
|
if (game.getAction().destroy(c, sa, !noRegen, table, params) && remDestroyed) {
|
||||||
card.addRemembered(CardUtil.getLKICopy(c, cachedMap));
|
card.addRemembered(CardUtil.getLKICopy(c, cachedMap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ import com.google.common.collect.Maps;
|
|||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameActionUtil;
|
import forge.game.GameActionUtil;
|
||||||
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.card.CardZoneTable;
|
import forge.game.card.CardZoneTable;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -72,13 +73,16 @@ public class DestroyEffect extends SpellAbilityEffect {
|
|||||||
card.clearRemembered();
|
card.clearRemembered();
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection tgtCards = getTargetCards(sa);
|
CardCollectionView tgtCards = getTargetCards(sa);
|
||||||
CardCollection untargetedCards = CardUtil.getRadiance(sa);
|
CardCollectionView untargetedCards = CardUtil.getRadiance(sa);
|
||||||
|
|
||||||
if (tgtCards.size() > 1) {
|
if (tgtCards.size() > 1) {
|
||||||
tgtCards = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, tgtCards, ZoneType.Graveyard, sa);
|
tgtCards = GameActionUtil.orderCardsByTheirOwners(game, tgtCards, ZoneType.Graveyard, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
|
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||||
|
|
||||||
CardZoneTable table = new CardZoneTable();
|
CardZoneTable table = new CardZoneTable();
|
||||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||||
for (final Card tgtC : tgtCards) {
|
for (final Card tgtC : tgtCards) {
|
||||||
@@ -90,24 +94,24 @@ public class DestroyEffect extends SpellAbilityEffect {
|
|||||||
if (gameCard == null || !tgtC.equalsWithTimestamp(gameCard)) {
|
if (gameCard == null || !tgtC.equalsWithTimestamp(gameCard)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
internalDestroy(gameCard, sa, table, cachedMap);
|
internalDestroy(gameCard, sa, table, cachedMap, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (untargetedCards.size() > 1) {
|
if (untargetedCards.size() > 1) {
|
||||||
untargetedCards = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, untargetedCards, ZoneType.Graveyard, sa);
|
untargetedCards = GameActionUtil.orderCardsByTheirOwners(game, untargetedCards, ZoneType.Graveyard, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Card unTgtC : untargetedCards) {
|
for (final Card unTgtC : untargetedCards) {
|
||||||
if (unTgtC.isInPlay()) {
|
if (unTgtC.isInPlay()) {
|
||||||
internalDestroy(unTgtC, sa, table, cachedMap);
|
internalDestroy(unTgtC, sa, table, cachedMap, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.triggerChangesZoneAll(game, sa);
|
table.triggerChangesZoneAll(game, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void internalDestroy(Card gameCard, SpellAbility sa, CardZoneTable table, Map<Integer, Card> cachedMap) {
|
protected void internalDestroy(Card gameCard, SpellAbility sa, CardZoneTable table, Map<Integer, Card> cachedMap, Map<AbilityKey, Object> params) {
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
final Game game = card.getGame();
|
final Game game = card.getGame();
|
||||||
|
|
||||||
@@ -122,9 +126,9 @@ public class DestroyEffect extends SpellAbilityEffect {
|
|||||||
card.addRemembered(gameCard.getAttachedCards());
|
card.addRemembered(gameCard.getAttachedCards());
|
||||||
}
|
}
|
||||||
if (sac) {
|
if (sac) {
|
||||||
destroyed = game.getAction().sacrifice(gameCard, sa, table) != null;
|
destroyed = game.getAction().sacrifice(gameCard, sa, table, params) != null;
|
||||||
} else {
|
} else {
|
||||||
destroyed = game.getAction().destroy(gameCard, sa, !noRegen, table);
|
destroyed = game.getAction().destroy(gameCard, sa, !noRegen, table, params);
|
||||||
}
|
}
|
||||||
if (destroyed && remDestroyed) {
|
if (destroyed && remDestroyed) {
|
||||||
card.addRemembered(gameCard);
|
card.addRemembered(gameCard);
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ public class DigEffect extends SpellAbilityEffect {
|
|||||||
} else if (destZone1.equals(ZoneType.Exile)) {
|
} else if (destZone1.equals(ZoneType.Exile)) {
|
||||||
if (sa.hasParam("ExileWithCounter")) {
|
if (sa.hasParam("ExileWithCounter")) {
|
||||||
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
|
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
|
||||||
1, player, true, counterTable);
|
1, player, sa, true, counterTable);
|
||||||
}
|
}
|
||||||
c.setExiledWith(effectHost);
|
c.setExiledWith(effectHost);
|
||||||
c.setExiledBy(effectHost.getController());
|
c.setExiledBy(effectHost.getController());
|
||||||
@@ -408,7 +408,7 @@ public class DigEffect extends SpellAbilityEffect {
|
|||||||
if (destZone2 == ZoneType.Exile) {
|
if (destZone2 == ZoneType.Exile) {
|
||||||
if (sa.hasParam("ExileWithCounter")) {
|
if (sa.hasParam("ExileWithCounter")) {
|
||||||
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
|
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")),
|
||||||
1, player, true, counterTable);
|
1, player, sa, true, counterTable);
|
||||||
}
|
}
|
||||||
c.setExiledWith(effectHost);
|
c.setExiledWith(effectHost);
|
||||||
c.setExiledBy(effectHost.getController());
|
c.setExiledBy(effectHost.getController());
|
||||||
|
|||||||
@@ -173,26 +173,25 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
|||||||
if (optionalFound && !p.getController().confirmAction(sa, null,
|
if (optionalFound && !p.getController().confirmAction(sa, null,
|
||||||
Localizer.getInstance().getMessage("lblDoYouWantPutCardToZone", foundDest.getTranslatedName()))) {
|
Localizer.getInstance().getMessage("lblDoYouWantPutCardToZone", foundDest.getTranslatedName()))) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
Card m = null;
|
||||||
|
if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) {
|
||||||
|
c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
|
||||||
|
m = game.getAction().moveTo(c.getController().getZone(foundDest), c, sa);
|
||||||
|
if (sa.hasParam("Tapped")) {
|
||||||
|
c.setTapped(true);
|
||||||
|
}
|
||||||
|
if (addToCombat(c, c.getController(), sa, "Attacking", "Blocking")) {
|
||||||
|
combatChanged = true;
|
||||||
|
}
|
||||||
|
} else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) {
|
||||||
|
//Don't do anything
|
||||||
} else {
|
} else {
|
||||||
Card m = null;
|
m = game.getAction().moveTo(foundDest, c, foundLibPos, sa);
|
||||||
if (sa.hasParam("GainControl") && foundDest.equals(ZoneType.Battlefield)) {
|
}
|
||||||
c.setController(sa.getActivatingPlayer(), game.getNextTimestamp());
|
revealed.remove(c);
|
||||||
m = game.getAction().moveTo(c.getController().getZone(foundDest), c, sa);
|
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
||||||
if (sa.hasParam("Tapped")) {
|
table.put(origin, m.getZone().getZoneType(), m);
|
||||||
c.setTapped(true);
|
|
||||||
}
|
|
||||||
if (addToCombat(c, c.getController(), sa, "Attacking", "Blocking")) {
|
|
||||||
combatChanged = true;
|
|
||||||
}
|
|
||||||
} else if (sa.hasParam("NoMoveFound") && foundDest.equals(ZoneType.Library)) {
|
|
||||||
//Don't do anything
|
|
||||||
} else {
|
|
||||||
m = game.getAction().moveTo(foundDest, c, foundLibPos, sa);
|
|
||||||
}
|
|
||||||
revealed.remove(c);
|
|
||||||
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
|
||||||
table.put(origin, m.getZone().getZoneType(), m);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ public class ETBReplacementEffect extends SpellAbilityEffect {
|
|||||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||||
params.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI));
|
params.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI));
|
||||||
params.put(AbilityKey.ReplacementEffect, sa.getReplacementEffect());
|
params.put(AbilityKey.ReplacementEffect, sa.getReplacementEffect());
|
||||||
sa.getActivatingPlayer().getGame().getAction().moveToPlay(card, card.getController(), sa, params);
|
final SpellAbility root = sa.getRootAbility();
|
||||||
|
SpellAbility cause = (SpellAbility) root.getReplacingObject(AbilityKey.Cause);
|
||||||
|
sa.getActivatingPlayer().getGame().getAction().moveToPlay(card, card.getController(), cause, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,9 +247,12 @@ public class EffectEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set Chosen Type
|
// Set Chosen Type
|
||||||
if (!hostCard.getChosenType().isEmpty()) {
|
if (hostCard.hasChosenType()) {
|
||||||
eff.setChosenType(hostCard.getChosenType());
|
eff.setChosenType(hostCard.getChosenType());
|
||||||
}
|
}
|
||||||
|
if (hostCard.hasChosenType2()) {
|
||||||
|
eff.setChosenType2(hostCard.getChosenType2());
|
||||||
|
}
|
||||||
|
|
||||||
// Set Chosen name
|
// Set Chosen name
|
||||||
if (!hostCard.getNamedCard().isEmpty()) {
|
if (!hostCard.getNamedCard().isEmpty()) {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class ExploreEffect extends SpellAbilityEffect {
|
|||||||
// if the card is not more in the game anymore
|
// if the card is not more in the game anymore
|
||||||
// this might still return true but its no problem
|
// this might still return true but its no problem
|
||||||
if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.equalsWithTimestamp(c)) {
|
if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.equalsWithTimestamp(c)) {
|
||||||
c.addCounter(CounterEnumType.P1P1, 1, pl, true, table);
|
c.addCounter(CounterEnumType.P1P1, 1, pl, sa, true, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,19 +65,18 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
|
|
||||||
if (isOptional && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblWouldYouLikeFight", CardTranslation.getTranslatedName(fighters.get(0).getName()), CardTranslation.getTranslatedName(fighters.get(1).getName())))) {
|
if (isOptional && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblWouldYouLikeFight", CardTranslation.getTranslatedName(fighters.get(0).getName()), CardTranslation.getTranslatedName(fighters.get(1).getName())))) {
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
dealDamage(sa, fighters.get(0), fighters.get(1));
|
|
||||||
|
|
||||||
for (Card c : fighters) {
|
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
|
||||||
runParams.put(AbilityKey.Fighter, c);
|
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Fight, runParams, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
|
||||||
runParams.put(AbilityKey.Fighters, fighters);
|
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.FightOnce, runParams, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dealDamage(sa, fighters.get(0), fighters.get(1));
|
||||||
|
|
||||||
|
for (Card c : fighters) {
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Fighter, c);
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.Fight, runParams, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Fighters, fighters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Card> getFighters(SpellAbility sa) {
|
private static List<Card> getFighters(SpellAbility sa) {
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
|
|||||||
Card lki = CardUtil.getLKICopy(gameCard);
|
Card lki = CardUtil.getLKICopy(gameCard);
|
||||||
lki.clearControllers();
|
lki.clearControllers();
|
||||||
lki.setOwner(sa.getActivatingPlayer());
|
lki.setOwner(sa.getActivatingPlayer());
|
||||||
// if this trigger is part of ETBReplacement it shouldn't run with LKI from incomplete zone change (Wall of Stolen Identity)
|
// if this trigger is part of ETBReplacement it shouldn't run with LKI from incomplete zone change (Mimic Vat + Wall of Stolen Identity)
|
||||||
final Card trigHost = sa.getRootAbility().getReplacementEffect() != null && sa.getRootAbility().getReplacementEffect().getMode().equals(ReplacementType.Moved) ? gameCard : lki;
|
final Card trigHost = sa.getRootAbility().getReplacementEffect() != null && sa.getRootAbility().getReplacementEffect().getMode().equals(ReplacementType.Moved) && gameCard.getZone() == null ? gameCard : lki;
|
||||||
final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, trigHost, sa.isIntrinsic(), null);
|
final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, trigHost, sa.isIntrinsic(), null);
|
||||||
immediateTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true));
|
immediateTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true));
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user