mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
Merge branch 'master' into 'oracle-updates-nonfunctional'
update nonfunctional changes branch See merge request core-developers/forge!937
This commit is contained in:
21
checkstyle.xml
Normal file
21
checkstyle.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||||
|
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Checkstyle is very configurable.
|
||||||
|
http://checkstyle.sf.net (or in your downloaded distribution).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name="Checker">
|
||||||
|
<module name="TreeWalker">
|
||||||
|
|
||||||
|
<module name="RedundantImport"/>
|
||||||
|
<module name="UnusedImports">
|
||||||
|
<!-- <property name="processJavadoc" value="false"/> -->
|
||||||
|
</module>
|
||||||
|
|
||||||
|
</module>
|
||||||
|
|
||||||
|
</module>
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
<name>forge-ai</name>
|
<name>forge-ai</name>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
<projects>
|
<projects>
|
||||||
|
<project>forge-game</project>
|
||||||
</projects>
|
</projects>
|
||||||
<buildSpec>
|
<buildSpec>
|
||||||
<buildCommand>
|
<buildCommand>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.11-SNAPSHOT</version>
|
<version>1.6.16-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-ai</artifactId>
|
<artifactId>forge-ai</artifactId>
|
||||||
@@ -29,4 +29,31 @@
|
|||||||
<version>3.6.1</version>
|
<version>3.6.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>checkstyle-validation</id>
|
||||||
|
<phase>validate</phase>
|
||||||
|
<configuration>
|
||||||
|
<configLocation>../checkstyle.xml</configLocation>
|
||||||
|
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<consoleOutput>true</consoleOutput>
|
||||||
|
<failsOnError>true</failsOnError>
|
||||||
|
<failOnViolation>true</failOnViolation>
|
||||||
|
</configuration>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -127,10 +127,12 @@ public class AiController {
|
|||||||
|
|
||||||
private List<SpellAbility> getPossibleETBCounters() {
|
private List<SpellAbility> getPossibleETBCounters() {
|
||||||
CardCollection all = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
CardCollection all = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
||||||
|
CardCollectionView ccvPlayerLibrary = player.getCardsIn(ZoneType.Library);
|
||||||
|
|
||||||
all.addAll(player.getCardsIn(ZoneType.Exile));
|
all.addAll(player.getCardsIn(ZoneType.Exile));
|
||||||
all.addAll(player.getCardsIn(ZoneType.Graveyard));
|
all.addAll(player.getCardsIn(ZoneType.Graveyard));
|
||||||
if (!player.getCardsIn(ZoneType.Library).isEmpty()) {
|
if (!ccvPlayerLibrary.isEmpty()) {
|
||||||
all.add(player.getCardsIn(ZoneType.Library).get(0));
|
all.add(ccvPlayerLibrary.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Player opp : player.getOpponents()) {
|
for (final Player opp : player.getOpponents()) {
|
||||||
@@ -153,14 +155,16 @@ public class AiController {
|
|||||||
|
|
||||||
// look for cards on the battlefield that should prevent the AI from using that spellability
|
// look for cards on the battlefield that should prevent the AI from using that spellability
|
||||||
private boolean checkCurseEffects(final SpellAbility sa) {
|
private boolean checkCurseEffects(final SpellAbility sa) {
|
||||||
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
CardCollectionView ccvGameBattlefield = game.getCardsIn(ZoneType.Battlefield);
|
||||||
|
for (final Card c : ccvGameBattlefield) {
|
||||||
if (c.hasSVar("AICurseEffect")) {
|
if (c.hasSVar("AICurseEffect")) {
|
||||||
final String curse = c.getSVar("AICurseEffect");
|
final String curse = c.getSVar("AICurseEffect");
|
||||||
final Card host = sa.getHostCard();
|
|
||||||
if ("NonActive".equals(curse) && !player.equals(game.getPhaseHandler().getPlayerTurn())) {
|
if ("NonActive".equals(curse) && !player.equals(game.getPhaseHandler().getPlayerTurn())) {
|
||||||
return true;
|
return true;
|
||||||
} else if ("DestroyCreature".equals(curse) && sa.isSpell() && host.isCreature()
|
} else {
|
||||||
&& !sa.getHostCard().hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
final Card host = sa.getHostCard();
|
||||||
|
if ("DestroyCreature".equals(curse) && sa.isSpell() && host.isCreature()
|
||||||
|
&& !host.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||||
return true;
|
return true;
|
||||||
} else if ("CounterEnchantment".equals(curse) && sa.isSpell() && host.isEnchantment()
|
} else if ("CounterEnchantment".equals(curse) && sa.isSpell() && host.isEnchantment()
|
||||||
&& CardFactoryUtil.isCounterable(host)) {
|
&& CardFactoryUtil.isCounterable(host)) {
|
||||||
@@ -169,29 +173,32 @@ public class AiController {
|
|||||||
&& host.getCMC() == c.getCounters(CounterType.CHARGE)) {
|
&& host.getCMC() == c.getCounters(CounterType.CHARGE)) {
|
||||||
return true;
|
return true;
|
||||||
} else if ("BazaarOfWonders".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)) {
|
} else if ("BazaarOfWonders".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)) {
|
||||||
for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
|
String hostName = host.getName();
|
||||||
if (!card.isToken() && card.getName().equals(host.getName())) {
|
for (Card card : ccvGameBattlefield) {
|
||||||
|
if (!card.isToken() && card.getName().equals(hostName)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Card card : game.getCardsIn(ZoneType.Graveyard)) {
|
for (Card card : game.getCardsIn(ZoneType.Graveyard)) {
|
||||||
if (card.getName().equals(host.getName())) {
|
if (card.getName().equals(hostName)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkETBEffects(final Card card, final SpellAbility sa, final ApiType api) {
|
public boolean checkETBEffects(final Card card, final SpellAbility sa, final ApiType api) {
|
||||||
boolean rightapi = false;
|
|
||||||
|
|
||||||
if (card.isCreature()
|
if (card.isCreature()
|
||||||
&& game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noCreatureETBTriggers)) {
|
&& game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noCreatureETBTriggers)) {
|
||||||
return api == null;
|
return api == null;
|
||||||
}
|
}
|
||||||
|
boolean rightapi = false;
|
||||||
|
String battlefield = ZoneType.Battlefield.toString();
|
||||||
|
Player activatingPlayer = sa.getActivatingPlayer();
|
||||||
|
|
||||||
// Trigger play improvements
|
// Trigger play improvements
|
||||||
for (final Trigger tr : card.getTriggers()) {
|
for (final Trigger tr : card.getTriggers()) {
|
||||||
@@ -202,21 +209,22 @@ public class AiController {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.get("Destination").equals(ZoneType.Battlefield.toString())) {
|
if (!params.get("Destination").equals(battlefield)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("ValidCard")) {
|
if (params.containsKey("ValidCard")) {
|
||||||
if (!params.get("ValidCard").contains("Self")) {
|
String validCard = params.get("ValidCard");
|
||||||
|
if (!validCard.contains("Self")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (params.get("ValidCard").contains("notkicked")) {
|
if (validCard.contains("notkicked")) {
|
||||||
if (sa.isKicked()) {
|
if (sa.isKicked()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (params.get("ValidCard").contains("kicked")) {
|
} else if (validCard.contains("kicked")) {
|
||||||
if (params.get("ValidCard").contains("kicked ")) { // want a specific kicker
|
if (validCard.contains("kicked ")) { // want a specific kicker
|
||||||
String s = params.get("ValidCard").split("kicked ")[1];
|
String s = validCard.split("kicked ")[1];
|
||||||
if ("1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
|
if ("1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
|
||||||
if ("2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
|
if ("2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
|
||||||
} else if (!sa.isKicked()) {
|
} else if (!sa.isKicked()) {
|
||||||
@@ -259,7 +267,7 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa != null) {
|
if (sa != null) {
|
||||||
exSA.setActivatingPlayer(sa.getActivatingPlayer());
|
exSA.setActivatingPlayer(activatingPlayer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
exSA.setActivatingPlayer(player);
|
exSA.setActivatingPlayer(player);
|
||||||
@@ -267,15 +275,13 @@ public class AiController {
|
|||||||
exSA.setTrigger(true);
|
exSA.setTrigger(true);
|
||||||
|
|
||||||
// for trigger test, need to ignore the conditions
|
// for trigger test, need to ignore the conditions
|
||||||
if (exSA.getConditions() != null) {
|
|
||||||
SpellAbilityCondition cons = exSA.getConditions();
|
SpellAbilityCondition cons = exSA.getConditions();
|
||||||
if (cons.getIsPresent() != null) {
|
if (cons != null) {
|
||||||
String pres = cons.getIsPresent();
|
String pres = cons.getIsPresent();
|
||||||
if ("Card.Self".equals(pres) || "Card.StrictlySelf".equals(pres)) {
|
if (pres != null && pres.matches("Card\\.(Strictly)?Self")) {
|
||||||
cons.setIsPresent(null);
|
cons.setIsPresent(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Run non-mandatory trigger.
|
// Run non-mandatory trigger.
|
||||||
// These checks only work if the Executing SpellAbility is an Ability_Sub.
|
// These checks only work if the Executing SpellAbility is an Ability_Sub.
|
||||||
@@ -297,21 +303,22 @@ public class AiController {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.get("Destination").equals(ZoneType.Battlefield.toString())) {
|
if (!params.get("Destination").equals(battlefield)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("ValidCard")) {
|
if (params.containsKey("ValidCard")) {
|
||||||
if (!params.get("ValidCard").contains("Self")) {
|
String validCard = params.get("ValidCard");
|
||||||
|
if (!validCard.contains("Self")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (params.get("ValidCard").contains("notkicked")) {
|
if (validCard.contains("notkicked")) {
|
||||||
if (sa.isKicked()) {
|
if (sa.isKicked()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (params.get("ValidCard").contains("kicked")) {
|
} else if (validCard.contains("kicked")) {
|
||||||
if (params.get("ValidCard").contains("kicked ")) { // want a specific kicker
|
if (validCard.contains("kicked ")) { // want a specific kicker
|
||||||
String s = params.get("ValidCard").split("kicked ")[1];
|
String s = validCard.split("kicked ")[1];
|
||||||
if ("1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
|
if ("1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
|
||||||
if ("2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
|
if ("2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
|
||||||
} else if (!sa.isKicked()) { // otherwise just any must be present
|
} else if (!sa.isKicked()) { // otherwise just any must be present
|
||||||
@@ -327,7 +334,7 @@ public class AiController {
|
|||||||
|
|
||||||
if (exSA != null) {
|
if (exSA != null) {
|
||||||
if (sa != null) {
|
if (sa != null) {
|
||||||
exSA.setActivatingPlayer(sa.getActivatingPlayer());
|
exSA.setActivatingPlayer(activatingPlayer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
exSA.setActivatingPlayer(player);
|
exSA.setActivatingPlayer(player);
|
||||||
@@ -375,8 +382,9 @@ public class AiController {
|
|||||||
if (landsInPlay.size() + landList.size() > max) {
|
if (landsInPlay.size() + landList.size() > max) {
|
||||||
for (Card c : allCards) {
|
for (Card c : allCards) {
|
||||||
for (SpellAbility sa : c.getSpellAbilities()) {
|
for (SpellAbility sa : c.getSpellAbilities()) {
|
||||||
if (sa.getPayCosts() != null) {
|
Cost payCosts = sa.getPayCosts();
|
||||||
for (CostPart part : sa.getPayCosts().getCostParts()) {
|
if (payCosts != null) {
|
||||||
|
for (CostPart part : payCosts.getCostParts()) {
|
||||||
if (part instanceof CostDiscard) {
|
if (part instanceof CostDiscard) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -390,10 +398,11 @@ public class AiController {
|
|||||||
landList = CardLists.filter(landList, new Predicate<Card>() {
|
landList = CardLists.filter(landList, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
|
CardCollectionView battlefield = player.getCardsIn(ZoneType.Battlefield);
|
||||||
canPlaySpellBasic(c, null);
|
canPlaySpellBasic(c, null);
|
||||||
if (c.getType().isLegendary() && !c.getName().equals("Flagstones of Trokair")) {
|
String name = c.getName();
|
||||||
final CardCollectionView list = player.getCardsIn(ZoneType.Battlefield);
|
if (c.getType().isLegendary() && !name.equals("Flagstones of Trokair")) {
|
||||||
if (Iterables.any(list, CardPredicates.nameEquals(c.getName()))) {
|
if (Iterables.any(battlefield, CardPredicates.nameEquals(name))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,7 +411,7 @@ public class AiController {
|
|||||||
final FCollectionView<SpellAbility> spellAbilities = c.getSpellAbilities();
|
final FCollectionView<SpellAbility> spellAbilities = c.getSpellAbilities();
|
||||||
|
|
||||||
final CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
|
final CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
|
||||||
CardCollection lands = new CardCollection(player.getCardsIn(ZoneType.Battlefield));
|
CardCollection lands = new CardCollection(battlefield);
|
||||||
lands.addAll(hand);
|
lands.addAll(hand);
|
||||||
lands = CardLists.filter(lands, CardPredicates.Presets.LANDS);
|
lands = CardLists.filter(lands, CardPredicates.Presets.LANDS);
|
||||||
int maxCmcInHand = Aggregates.max(hand, CardPredicates.Accessors.fnGetCmc);
|
int maxCmcInHand = Aggregates.max(hand, CardPredicates.Accessors.fnGetCmc);
|
||||||
@@ -565,14 +574,17 @@ public class AiController {
|
|||||||
Collections.sort(all, saComparator); // put best spells first
|
Collections.sort(all, saComparator); // put best spells first
|
||||||
|
|
||||||
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) {
|
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) {
|
||||||
if (sa.getApi() == ApiType.Counter || sa.getApi() == exceptSA) {
|
ApiType saApi = sa.getApi();
|
||||||
|
|
||||||
|
if (saApi == ApiType.Counter || saApi == exceptSA) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sa.setActivatingPlayer(player);
|
sa.setActivatingPlayer(player);
|
||||||
// TODO: this currently only works as a limited prediction of permanent spells.
|
// TODO: this currently only works as a limited prediction of permanent spells.
|
||||||
// Ideally this should cast canPlaySa to determine that the AI is truly able/willing to cast a spell,
|
// Ideally this should cast canPlaySa to determine that the AI is truly able/willing to cast a spell,
|
||||||
// but that is currently difficult to implement due to various side effects leading to stack overflow.
|
// but that is currently difficult to implement due to various side effects leading to stack overflow.
|
||||||
if (!ComputerUtil.castPermanentInMain1(player, sa) && sa.getHostCard() != null && !sa.getHostCard().isLand() && ComputerUtilCost.canPayCost(sa, player)) {
|
Card host = sa.getHostCard();
|
||||||
|
if (!ComputerUtil.castPermanentInMain1(player, sa) && host != null && !host.isLand() && ComputerUtilCost.canPayCost(sa, player)) {
|
||||||
if (sa instanceof SpellPermanent) {
|
if (sa instanceof SpellPermanent) {
|
||||||
return sa;
|
return sa;
|
||||||
}
|
}
|
||||||
@@ -800,8 +812,7 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use Surge and Prowl costs when able to
|
// use Surge and Prowl costs when able to
|
||||||
if (sa.isSurged() ||
|
if (sa.isSurged() || sa.isProwl()) {
|
||||||
(sa.getRestrictions().getProwlTypes() != null && !sa.getRestrictions().getProwlTypes().isEmpty())) {
|
|
||||||
p += 9;
|
p += 9;
|
||||||
}
|
}
|
||||||
// sort planeswalker abilities with most costly first
|
// sort planeswalker abilities with most costly first
|
||||||
|
|||||||
@@ -514,20 +514,27 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
Integer c = cost.convertAmount();
|
Integer c = cost.convertAmount();
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
if (ability.getSVar(cost.getAmount()).equals("XChoice")) {
|
if (ability.getSVar(cost.getAmount()).equals("XChoice")) {
|
||||||
if ("SacToReduceCost".equals(ability.getParam("AILogic"))) {
|
String logic = ability.getParamOrDefault("AILogic", "");
|
||||||
|
if ("SacToReduceCost".equals(logic)) {
|
||||||
// e.g. Torgaar, Famine Incarnate
|
// e.g. Torgaar, Famine Incarnate
|
||||||
// TODO: currently returns an empty list, so the AI doesn't sacrifice anything. Trying to make
|
// TODO: currently returns an empty list, so the AI doesn't sacrifice anything. Trying to make
|
||||||
// the AI decide on creatures to sac makes the AI sacrifice them, but the cost is not reduced and the
|
// the AI decide on creatures to sac makes the AI sacrifice them, but the cost is not reduced and the
|
||||||
// AI pays the full mana cost anyway (despite sacrificing creatures).
|
// AI pays the full mana cost anyway (despite sacrificing creatures).
|
||||||
return PaymentDecision.card(new CardCollection());
|
return PaymentDecision.card(new CardCollection());
|
||||||
}
|
} else if (!logic.isEmpty() && !logic.equals("Never")) {
|
||||||
|
// If at least some other AI logic is specified, assume that the AI for that API knows how
|
||||||
|
// to define ChosenX and thus honor that value.
|
||||||
|
// Cards which have no special logic for this yet but which do work in a simple/suboptimal way
|
||||||
|
// are currently conventionally flagged with AILogic$ DoSacrifice.
|
||||||
|
c = AbilityUtils.calculateAmount(source, source.getSVar("ChosenX"), null);
|
||||||
|
} else {
|
||||||
// Other cards are assumed to be flagged RemAIDeck for now
|
// Other cards are assumed to be flagged RemAIDeck for now
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final AiController aic = ((PlayerControllerAi)player.getController()).getAi();
|
final AiController aic = ((PlayerControllerAi)player.getController()).getAi();
|
||||||
CardCollectionView list = aic.chooseSacrificeType(cost.getType(), ability, c);
|
CardCollectionView list = aic.chooseSacrificeType(cost.getType(), ability, c);
|
||||||
return PaymentDecision.card(list);
|
return PaymentDecision.card(list);
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ public enum AiProps { /** */
|
|||||||
SCRY_EVALTHR_CMC_THRESHOLD ("3"), /** */
|
SCRY_EVALTHR_CMC_THRESHOLD ("3"), /** */
|
||||||
SCRY_IMMEDIATELY_UNCASTABLE_TO_BOTTOM ("false"), /** */
|
SCRY_IMMEDIATELY_UNCASTABLE_TO_BOTTOM ("false"), /** */
|
||||||
SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF ("1"), /** */
|
SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF ("1"), /** */
|
||||||
|
SURVEIL_NUM_CARDS_IN_LIBRARY_TO_BAIL ("10"), /** */
|
||||||
COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION ("true"), /** */
|
COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION ("true"), /** */
|
||||||
COMBAT_ATTRITION_ATTACK_EVASION_PREDICTION ("true"), /** */
|
COMBAT_ATTRITION_ATTACK_EVASION_PREDICTION ("true"), /** */
|
||||||
CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT ("true"), /** */
|
CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT ("true"), /** */
|
||||||
|
|||||||
@@ -95,7 +95,9 @@ public class ComputerUtil {
|
|||||||
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
sa.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sa.isCopied()) {
|
||||||
sa.resetPaidHash();
|
sa.resetPaidHash();
|
||||||
|
}
|
||||||
|
|
||||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||||
CharmEffect.makeChoices(sa);
|
CharmEffect.makeChoices(sa);
|
||||||
@@ -260,6 +262,10 @@ public class ComputerUtil {
|
|||||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||||
newSA.setHostCard(game.getAction().moveToStack(source, sa));
|
newSA.setHostCard(game.getAction().moveToStack(source, sa));
|
||||||
|
|
||||||
|
if (newSA.getApi() == ApiType.Charm && !newSA.isWrapper()) {
|
||||||
|
CharmEffect.makeChoices(newSA);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final CostPayment pay = new CostPayment(newSA.getPayCosts(), newSA);
|
final CostPayment pay = new CostPayment(newSA.getPayCosts(), newSA);
|
||||||
@@ -2630,7 +2636,7 @@ public class ComputerUtil {
|
|||||||
|| (type == CounterType.TIME && (!c.isInPlay() || "Chronozoa".equals(c.getName())))
|
|| (type == CounterType.TIME && (!c.isInPlay() || "Chronozoa".equals(c.getName())))
|
||||||
|| type == CounterType.GOLD || type == CounterType.MUSIC || type == CounterType.PUPA
|
|| type == CounterType.GOLD || type == CounterType.MUSIC || type == CounterType.PUPA
|
||||||
|| type == CounterType.PARALYZATION || type == CounterType.SHELL || type == CounterType.SLEEP
|
|| type == CounterType.PARALYZATION || type == CounterType.SHELL || type == CounterType.SLEEP
|
||||||
|| type == CounterType.SLEIGHT || type == CounterType.WAGE;
|
|| type == CounterType.SLUMBER || type == CounterType.SLEIGHT || type == CounterType.WAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this countertypes has no effect
|
// this countertypes has no effect
|
||||||
|
|||||||
@@ -1573,10 +1573,10 @@ public class ComputerUtilCard {
|
|||||||
pumped.addNewPT(c.getCurrentPower(), c.getCurrentToughness(), timestamp);
|
pumped.addNewPT(c.getCurrentPower(), c.getCurrentToughness(), timestamp);
|
||||||
pumped.addTempPowerBoost(c.getTempPowerBoost() + power + berserkPower);
|
pumped.addTempPowerBoost(c.getTempPowerBoost() + power + berserkPower);
|
||||||
pumped.addTempToughnessBoost(c.getTempToughnessBoost() + toughness);
|
pumped.addTempToughnessBoost(c.getTempToughnessBoost() + toughness);
|
||||||
pumped.addChangedCardKeywords(kws, new ArrayList<String>(), 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), c, true);
|
pumped.addCounterFireNoEvents(ct, c.getCounters(ct), ai, true);
|
||||||
}
|
}
|
||||||
//Copies tap-state and extra keywords (auras, equipment, etc.)
|
//Copies tap-state and extra keywords (auras, equipment, etc.)
|
||||||
if (c.isTapped()) {
|
if (c.isTapped()) {
|
||||||
@@ -1596,7 +1596,7 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
final long timestamp2 = c.getGame().getNextTimestamp(); //is this necessary or can the timestamp be re-used?
|
final long timestamp2 = c.getGame().getNextTimestamp(); //is this necessary or can the timestamp be re-used?
|
||||||
pumped.addChangedCardKeywordsInternal(toCopy, Lists.<KeywordInterface>newArrayList(), false, timestamp2, true);
|
pumped.addChangedCardKeywordsInternal(toCopy, null, false, false, timestamp2, true);
|
||||||
ComputerUtilCard.applyStaticContPT(ai.getGame(), pumped, new CardCollection(c));
|
ComputerUtilCard.applyStaticContPT(ai.getGame(), pumped, new CardCollection(c));
|
||||||
return pumped;
|
return pumped;
|
||||||
}
|
}
|
||||||
@@ -1625,6 +1625,9 @@ public class ComputerUtilCard {
|
|||||||
if (!params.containsKey("Affected")) {
|
if (!params.containsKey("Affected")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!params.containsKey("AddPower") && !params.containsKey("AddToughness")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final String valid = params.get("Affected");
|
final String valid = params.get("Affected");
|
||||||
if (!vCard.isValid(valid, c.getController(), c, null)) {
|
if (!vCard.isValid(valid, c.getController(), c, null)) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -975,12 +975,20 @@ public class ComputerUtilCombat {
|
|||||||
final Map<String, String> trigParams = trigger.getMapParams();
|
final Map<String, String> trigParams = trigger.getMapParams();
|
||||||
final Card source = trigger.getHostCard();
|
final Card source = trigger.getHostCard();
|
||||||
|
|
||||||
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)
|
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
|
||||||
|| !trigParams.containsKey("Execute")) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> abilityParams = null;
|
||||||
|
if (trigger.getOverridingAbility() != null) {
|
||||||
|
abilityParams = trigger.getOverridingAbility().getMapParams();
|
||||||
|
} else if (trigParams.containsKey("Execute")) {
|
||||||
final String ability = source.getSVar(trigParams.get("Execute"));
|
final String ability = source.getSVar(trigParams.get("Execute"));
|
||||||
final Map<String, String> abilityParams = AbilityFactory.getMapParams(ability);
|
abilityParams = AbilityFactory.getMapParams(ability);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (abilityParams.containsKey("AB") && !abilityParams.get("AB").equals("Pump")) {
|
if (abilityParams.containsKey("AB") && !abilityParams.get("AB").equals("Pump")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1098,12 +1106,20 @@ public class ComputerUtilCombat {
|
|||||||
final Map<String, String> trigParams = trigger.getMapParams();
|
final Map<String, String> trigParams = trigger.getMapParams();
|
||||||
final Card source = trigger.getHostCard();
|
final Card source = trigger.getHostCard();
|
||||||
|
|
||||||
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)
|
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
|
||||||
|| !trigParams.containsKey("Execute")) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> abilityParams = null;
|
||||||
|
if (trigger.getOverridingAbility() != null) {
|
||||||
|
abilityParams = trigger.getOverridingAbility().getMapParams();
|
||||||
|
} else if (trigParams.containsKey("Execute")) {
|
||||||
final String ability = source.getSVar(trigParams.get("Execute"));
|
final String ability = source.getSVar(trigParams.get("Execute"));
|
||||||
final Map<String, String> abilityParams = AbilityFactory.getMapParams(ability);
|
abilityParams = AbilityFactory.getMapParams(ability);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
String abType = "";
|
String abType = "";
|
||||||
if (abilityParams.containsKey("AB")) {
|
if (abilityParams.containsKey("AB")) {
|
||||||
abType = abilityParams.get("AB");
|
abType = abilityParams.get("AB");
|
||||||
@@ -1311,12 +1327,20 @@ public class ComputerUtilCombat {
|
|||||||
final Map<String, String> trigParams = trigger.getMapParams();
|
final Map<String, String> trigParams = trigger.getMapParams();
|
||||||
final Card source = trigger.getHostCard();
|
final Card source = trigger.getHostCard();
|
||||||
|
|
||||||
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)
|
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)) {
|
||||||
|| !trigParams.containsKey("Execute")) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> abilityParams = null;
|
||||||
|
if (trigger.getOverridingAbility() != null) {
|
||||||
|
abilityParams = trigger.getOverridingAbility().getMapParams();
|
||||||
|
} else if (trigParams.containsKey("Execute")) {
|
||||||
final String ability = source.getSVar(trigParams.get("Execute"));
|
final String ability = source.getSVar(trigParams.get("Execute"));
|
||||||
final Map<String, String> abilityParams = AbilityFactory.getMapParams(ability);
|
abilityParams = AbilityFactory.getMapParams(ability);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (abilityParams.containsKey("ValidTgts") || abilityParams.containsKey("Tgt")) {
|
if (abilityParams.containsKey("ValidTgts") || abilityParams.containsKey("Tgt")) {
|
||||||
continue; // targeted pumping not supported
|
continue; // targeted pumping not supported
|
||||||
}
|
}
|
||||||
@@ -1330,7 +1354,14 @@ public class ComputerUtilCombat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (abilityParams.containsKey("Cost")) {
|
if (abilityParams.containsKey("Cost")) {
|
||||||
final SpellAbility sa = AbilityFactory.getAbility(ability, source);
|
SpellAbility sa = null;
|
||||||
|
if (trigger.getOverridingAbility() != null) {
|
||||||
|
sa = trigger.getOverridingAbility();
|
||||||
|
} else {
|
||||||
|
final String ability = source.getSVar(trigParams.get("Execute"));
|
||||||
|
sa = AbilityFactory.getAbility(ability, source);
|
||||||
|
}
|
||||||
|
|
||||||
sa.setActivatingPlayer(source.getController());
|
sa.setActivatingPlayer(source.getController());
|
||||||
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
|
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1514,12 +1545,20 @@ public class ComputerUtilCombat {
|
|||||||
final Map<String, String> trigParams = trigger.getMapParams();
|
final Map<String, String> trigParams = trigger.getMapParams();
|
||||||
final Card source = trigger.getHostCard();
|
final Card source = trigger.getHostCard();
|
||||||
|
|
||||||
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)
|
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)) {
|
||||||
|| !trigParams.containsKey("Execute")) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> abilityParams = null;
|
||||||
|
if (trigger.getOverridingAbility() != null) {
|
||||||
|
abilityParams = trigger.getOverridingAbility().getMapParams();
|
||||||
|
} else if (trigParams.containsKey("Execute")) {
|
||||||
final String ability = source.getSVar(trigParams.get("Execute"));
|
final String ability = source.getSVar(trigParams.get("Execute"));
|
||||||
final Map<String, String> abilityParams = AbilityFactory.getMapParams(ability);
|
abilityParams = AbilityFactory.getMapParams(ability);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (abilityParams.containsKey("ValidTgts") || abilityParams.containsKey("Tgt")) {
|
if (abilityParams.containsKey("ValidTgts") || abilityParams.containsKey("Tgt")) {
|
||||||
continue; // targeted pumping not supported
|
continue; // targeted pumping not supported
|
||||||
}
|
}
|
||||||
@@ -1552,7 +1591,14 @@ public class ComputerUtilCombat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (abilityParams.containsKey("Cost")) {
|
if (abilityParams.containsKey("Cost")) {
|
||||||
final SpellAbility sa = AbilityFactory.getAbility(ability, source);
|
SpellAbility sa = null;
|
||||||
|
if (trigger.getOverridingAbility() != null) {
|
||||||
|
sa = trigger.getOverridingAbility();
|
||||||
|
} else {
|
||||||
|
final String ability = source.getSVar(trigParams.get("Execute"));
|
||||||
|
sa = AbilityFactory.getAbility(ability, source);
|
||||||
|
}
|
||||||
|
|
||||||
sa.setActivatingPlayer(source.getController());
|
sa.setActivatingPlayer(source.getController());
|
||||||
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
|
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import com.google.common.collect.Lists;
|
|||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import forge.ai.ability.AnimateAi;
|
import forge.ai.ability.AnimateAi;
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
import forge.game.GameActionUtil;
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.google.common.collect.Maps;
|
|||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
|
import forge.card.MagicColor;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
@@ -19,8 +20,10 @@ import forge.game.combat.Combat;
|
|||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
import forge.game.event.GameEventAttackersDeclared;
|
import forge.game.event.GameEventAttackersDeclared;
|
||||||
import forge.game.event.GameEventCombatChanged;
|
import forge.game.event.GameEventCombatChanged;
|
||||||
|
import forge.game.mana.ManaPool;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.AbilityManaPart;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.game.zone.PlayerZone;
|
import forge.game.zone.PlayerZone;
|
||||||
@@ -52,6 +55,14 @@ public abstract class GameState {
|
|||||||
private int computerLife = -1;
|
private int computerLife = -1;
|
||||||
private String humanCounters = "";
|
private String humanCounters = "";
|
||||||
private String computerCounters = "";
|
private String computerCounters = "";
|
||||||
|
private String humanManaPool = "";
|
||||||
|
private String computerManaPool = "";
|
||||||
|
private String humanPersistentMana = "";
|
||||||
|
private String computerPersistentMana = "";
|
||||||
|
private int humanLandsPlayed = 0;
|
||||||
|
private int computerLandsPlayed = 0;
|
||||||
|
private int humanLandsPlayedLastTurn = 0;
|
||||||
|
private int computerLandsPlayedLastTurn = 0;
|
||||||
|
|
||||||
private boolean puzzleCreatorState = false;
|
private boolean puzzleCreatorState = false;
|
||||||
|
|
||||||
@@ -60,6 +71,7 @@ public abstract class GameState {
|
|||||||
|
|
||||||
private final Map<Integer, Card> idToCard = new HashMap<>();
|
private final Map<Integer, Card> idToCard = new HashMap<>();
|
||||||
private final Map<Card, Integer> cardToAttachId = new HashMap<>();
|
private final Map<Card, Integer> cardToAttachId = new HashMap<>();
|
||||||
|
private final Map<Card, Integer> cardToEnchantPlayerId = new HashMap<>();
|
||||||
private final Map<Card, Integer> markedDamage = new HashMap<>();
|
private final Map<Card, Integer> markedDamage = new HashMap<>();
|
||||||
private final Map<Card, List<String>> cardToChosenClrs = new HashMap<>();
|
private final Map<Card, List<String>> cardToChosenClrs = new HashMap<>();
|
||||||
private final Map<Card, String> cardToChosenType = new HashMap<>();
|
private final Map<Card, String> cardToChosenType = new HashMap<>();
|
||||||
@@ -79,6 +91,8 @@ public abstract class GameState {
|
|||||||
private String tChangePlayer = "NONE";
|
private String tChangePlayer = "NONE";
|
||||||
private String tChangePhase = "NONE";
|
private String tChangePhase = "NONE";
|
||||||
|
|
||||||
|
private String tAdvancePhase = "NONE";
|
||||||
|
|
||||||
private String precastHuman = null;
|
private String precastHuman = null;
|
||||||
private String precastAI = null;
|
private String precastAI = null;
|
||||||
|
|
||||||
@@ -112,6 +126,10 @@ public abstract class GameState {
|
|||||||
|
|
||||||
sb.append(TextUtil.concatNoSpace("humanlife=", String.valueOf(humanLife), "\n"));
|
sb.append(TextUtil.concatNoSpace("humanlife=", String.valueOf(humanLife), "\n"));
|
||||||
sb.append(TextUtil.concatNoSpace("ailife=", String.valueOf(computerLife), "\n"));
|
sb.append(TextUtil.concatNoSpace("ailife=", String.valueOf(computerLife), "\n"));
|
||||||
|
sb.append(TextUtil.concatNoSpace("humanlandsplayed=", String.valueOf(humanLandsPlayed), "\n"));
|
||||||
|
sb.append(TextUtil.concatNoSpace("ailandsplayed=", String.valueOf(computerLandsPlayed), "\n"));
|
||||||
|
sb.append(TextUtil.concatNoSpace("humanlandsplayedlastturn=", String.valueOf(humanLandsPlayedLastTurn), "\n"));
|
||||||
|
sb.append(TextUtil.concatNoSpace("ailandsplayedlastturn=", String.valueOf(computerLandsPlayedLastTurn), "\n"));
|
||||||
sb.append(TextUtil.concatNoSpace("turn=", String.valueOf(turn), "\n"));
|
sb.append(TextUtil.concatNoSpace("turn=", String.valueOf(turn), "\n"));
|
||||||
|
|
||||||
if (!humanCounters.isEmpty()) {
|
if (!humanCounters.isEmpty()) {
|
||||||
@@ -121,6 +139,13 @@ public abstract class GameState {
|
|||||||
sb.append(TextUtil.concatNoSpace("aicounters=", computerCounters, "\n"));
|
sb.append(TextUtil.concatNoSpace("aicounters=", computerCounters, "\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!humanManaPool.isEmpty()) {
|
||||||
|
sb.append(TextUtil.concatNoSpace("humanmanapool=", humanManaPool, "\n"));
|
||||||
|
}
|
||||||
|
if (!computerManaPool.isEmpty()) {
|
||||||
|
sb.append(TextUtil.concatNoSpace("aimanapool=", humanManaPool, "\n"));
|
||||||
|
}
|
||||||
|
|
||||||
sb.append(TextUtil.concatNoSpace("activeplayer=", tChangePlayer, "\n"));
|
sb.append(TextUtil.concatNoSpace("activeplayer=", tChangePlayer, "\n"));
|
||||||
sb.append(TextUtil.concatNoSpace("activephase=", tChangePhase, "\n"));
|
sb.append(TextUtil.concatNoSpace("activephase=", tChangePhase, "\n"));
|
||||||
appendCards(humanCardTexts, "human", sb);
|
appendCards(humanCardTexts, "human", sb);
|
||||||
@@ -147,8 +172,14 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
humanLife = human.getLife();
|
humanLife = human.getLife();
|
||||||
computerLife = ai.getLife();
|
computerLife = ai.getLife();
|
||||||
|
humanLandsPlayed = human.getLandsPlayedThisTurn();
|
||||||
|
computerLandsPlayed = ai.getLandsPlayedThisTurn();
|
||||||
|
humanLandsPlayedLastTurn = human.getLandsPlayedLastTurn();
|
||||||
|
computerLandsPlayedLastTurn = ai.getLandsPlayedLastTurn();
|
||||||
humanCounters = countersToString(human.getCounters());
|
humanCounters = countersToString(human.getCounters());
|
||||||
computerCounters = countersToString(ai.getCounters());
|
computerCounters = countersToString(ai.getCounters());
|
||||||
|
humanManaPool = processManaPool(human.getManaPool());
|
||||||
|
computerManaPool = processManaPool(ai.getManaPool());
|
||||||
|
|
||||||
tChangePlayer = game.getPhaseHandler().getPlayerTurn() == ai ? "ai" : "human";
|
tChangePlayer = game.getPhaseHandler().getPlayerTurn() == ai ? "ai" : "human";
|
||||||
tChangePhase = game.getPhaseHandler().getPhase().toString();
|
tChangePhase = game.getPhaseHandler().getPhase().toString();
|
||||||
@@ -266,6 +297,12 @@ public abstract class GameState {
|
|||||||
} else if (c.getEnchantingCard() != null) {
|
} else if (c.getEnchantingCard() != null) {
|
||||||
newText.append("|Attaching:").append(c.getEnchantingCard().getId());
|
newText.append("|Attaching:").append(c.getEnchantingCard().getId());
|
||||||
}
|
}
|
||||||
|
if (c.getEnchantingPlayer() != null) {
|
||||||
|
// TODO: improve this for game states with more than two players
|
||||||
|
newText.append("|EnchantingPlayer:");
|
||||||
|
Player p = c.getEnchantingPlayer();
|
||||||
|
newText.append(p.getController().isAI() ? "AI" : "HUMAN");
|
||||||
|
}
|
||||||
|
|
||||||
if (c.getDamage() > 0) {
|
if (c.getDamage() > 0) {
|
||||||
newText.append("|Damage:").append(c.getDamage());
|
newText.append("|Damage:").append(c.getDamage());
|
||||||
@@ -388,8 +425,10 @@ public abstract class GameState {
|
|||||||
if (categoryName.startsWith("active")) {
|
if (categoryName.startsWith("active")) {
|
||||||
if (categoryName.endsWith("player"))
|
if (categoryName.endsWith("player"))
|
||||||
tChangePlayer = categoryValue.trim().toLowerCase();
|
tChangePlayer = categoryValue.trim().toLowerCase();
|
||||||
if (categoryName.endsWith("phase"))
|
else if (categoryName.endsWith("phase"))
|
||||||
tChangePhase = categoryValue.trim().toUpperCase();
|
tChangePhase = categoryValue.trim().toUpperCase();
|
||||||
|
else if (categoryName.endsWith("phaseadvance"))
|
||||||
|
tAdvancePhase = categoryValue.trim().toUpperCase();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,6 +452,20 @@ public abstract class GameState {
|
|||||||
computerCounters = categoryValue;
|
computerCounters = categoryValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (categoryName.endsWith("landsplayed")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanLandsPlayed = Integer.parseInt(categoryValue);
|
||||||
|
else
|
||||||
|
computerLandsPlayed = Integer.parseInt(categoryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (categoryName.endsWith("landsplayedlastturn")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanLandsPlayedLastTurn = Integer.parseInt(categoryValue);
|
||||||
|
else
|
||||||
|
computerLandsPlayedLastTurn = Integer.parseInt(categoryValue);
|
||||||
|
}
|
||||||
|
|
||||||
else if (categoryName.endsWith("play") || categoryName.endsWith("battlefield")) {
|
else if (categoryName.endsWith("play") || categoryName.endsWith("battlefield")) {
|
||||||
if (isHuman)
|
if (isHuman)
|
||||||
humanCardTexts.put(ZoneType.Battlefield, categoryValue);
|
humanCardTexts.put(ZoneType.Battlefield, categoryValue);
|
||||||
@@ -465,6 +518,21 @@ public abstract class GameState {
|
|||||||
else
|
else
|
||||||
precastAI = categoryValue;
|
precastAI = categoryValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (categoryName.endsWith("manapool")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanManaPool = categoryValue;
|
||||||
|
else
|
||||||
|
computerManaPool = categoryValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (categoryName.endsWith("persistentmana")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanPersistentMana = categoryValue;
|
||||||
|
else
|
||||||
|
computerPersistentMana = categoryValue;
|
||||||
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
System.out.println("Unknown key: " + categoryName);
|
System.out.println("Unknown key: " + categoryName);
|
||||||
}
|
}
|
||||||
@@ -485,6 +553,7 @@ public abstract class GameState {
|
|||||||
|
|
||||||
idToCard.clear();
|
idToCard.clear();
|
||||||
cardToAttachId.clear();
|
cardToAttachId.clear();
|
||||||
|
cardToEnchantPlayerId.clear();
|
||||||
cardToRememberedId.clear();
|
cardToRememberedId.clear();
|
||||||
cardToExiledWithId.clear();
|
cardToExiledWithId.clear();
|
||||||
markedDamage.clear();
|
markedDamage.clear();
|
||||||
@@ -493,12 +562,18 @@ public abstract class GameState {
|
|||||||
cardToScript.clear();
|
cardToScript.clear();
|
||||||
cardAttackMap.clear();
|
cardAttackMap.clear();
|
||||||
|
|
||||||
Player newPlayerTurn = tChangePlayer.equals("human") ? human : tChangePlayer.equals("ai") ? ai : null;
|
Player newPlayerTurn = tChangePlayer.equalsIgnoreCase("human") ? human : tChangePlayer.equalsIgnoreCase("ai") ? ai : null;
|
||||||
PhaseType newPhase = tChangePhase.equals("none") ? null : PhaseType.smartValueOf(tChangePhase);
|
PhaseType newPhase = tChangePhase.equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tChangePhase);
|
||||||
|
PhaseType advPhase = tAdvancePhase.equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tAdvancePhase);
|
||||||
|
|
||||||
// Set stack to resolving so things won't trigger/effects be checked right away
|
// Set stack to resolving so things won't trigger/effects be checked right away
|
||||||
game.getStack().setResolving(true);
|
game.getStack().setResolving(true);
|
||||||
|
|
||||||
|
updateManaPool(human, humanManaPool, true, false);
|
||||||
|
updateManaPool(ai, computerManaPool, true, false);
|
||||||
|
updateManaPool(human, humanPersistentMana, false, true);
|
||||||
|
updateManaPool(ai, computerPersistentMana, false, true);
|
||||||
|
|
||||||
if (!humanCounters.isEmpty()) {
|
if (!humanCounters.isEmpty()) {
|
||||||
applyCountersToGameEntity(human, humanCounters);
|
applyCountersToGameEntity(human, humanCounters);
|
||||||
}
|
}
|
||||||
@@ -510,8 +585,8 @@ public abstract class GameState {
|
|||||||
|
|
||||||
game.getTriggerHandler().setSuppressAllTriggers(true);
|
game.getTriggerHandler().setSuppressAllTriggers(true);
|
||||||
|
|
||||||
setupPlayerState(humanLife, humanCardTexts, human);
|
setupPlayerState(humanLife, humanCardTexts, human, humanLandsPlayed, humanLandsPlayedLastTurn);
|
||||||
setupPlayerState(computerLife, aiCardTexts, ai);
|
setupPlayerState(computerLife, aiCardTexts, ai, computerLandsPlayed, computerLandsPlayedLastTurn);
|
||||||
|
|
||||||
handleCardAttachments();
|
handleCardAttachments();
|
||||||
handleChosenEntities();
|
handleChosenEntities();
|
||||||
@@ -531,9 +606,50 @@ public abstract class GameState {
|
|||||||
|
|
||||||
game.getStack().setResolving(false);
|
game.getStack().setResolving(false);
|
||||||
|
|
||||||
|
// Advance to a certain phase, activating all triggered abilities
|
||||||
|
if (advPhase != null) {
|
||||||
|
game.getPhaseHandler().devAdvanceToPhase(advPhase);
|
||||||
|
}
|
||||||
|
|
||||||
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
|
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String processManaPool(ManaPool manaPool) {
|
||||||
|
String mana = "";
|
||||||
|
for (final byte c : MagicColor.WUBRGC) {
|
||||||
|
int amount = manaPool.getAmountOfColor(c);
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
mana += MagicColor.toShortString(c) + " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mana.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateManaPool(Player p, String manaDef, boolean clearPool, boolean persistent) {
|
||||||
|
Game game = p.getGame();
|
||||||
|
if (clearPool) {
|
||||||
|
p.getManaPool().clearPool(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manaDef.isEmpty()) {
|
||||||
|
final Card dummy = new Card(-777777, game);
|
||||||
|
dummy.setOwner(p);
|
||||||
|
final Map<String, String> produced = Maps.newHashMap();
|
||||||
|
produced.put("Produced", manaDef);
|
||||||
|
if (persistent) {
|
||||||
|
produced.put("PersistentMana", "True");
|
||||||
|
}
|
||||||
|
final AbilityManaPart abMana = new AbilityManaPart(dummy, produced);
|
||||||
|
game.getAction().invoke(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
abMana.produceMana(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleCombat(final Game game, final Player attackingPlayer, final Player defendingPlayer, final boolean toDeclareBlockers) {
|
private void handleCombat(final Game game, final Player attackingPlayer, final Player defendingPlayer, final boolean toDeclareBlockers) {
|
||||||
// First we need to ensure that all attackers are declared in the Declare Attackers step,
|
// First we need to ensure that all attackers are declared in the Declare Attackers step,
|
||||||
// even if proceeding straight to Declare Blockers
|
// even if proceeding straight to Declare Blockers
|
||||||
@@ -864,6 +980,16 @@ public abstract class GameState {
|
|||||||
attacher.fortifyCard(attachedTo);
|
attacher.fortifyCard(attachedTo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enchant players by ID
|
||||||
|
for(Entry<Card, Integer> entry : cardToEnchantPlayerId.entrySet()) {
|
||||||
|
// TODO: improve this for game states with more than two players
|
||||||
|
Card attacher = entry.getKey();
|
||||||
|
Game game = attacher.getGame();
|
||||||
|
Player attachedTo = entry.getValue() == TARGET_AI ? game.getPlayers().get(1) : game.getPlayers().get(0);
|
||||||
|
|
||||||
|
attacher.enchantEntity(attachedTo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyCountersToGameEntity(GameEntity entity, String counterString) {
|
private void applyCountersToGameEntity(GameEntity entity, String counterString) {
|
||||||
@@ -875,7 +1001,7 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPlayerState(int life, Map<ZoneType, String> cardTexts, final Player p) {
|
private void setupPlayerState(int life, Map<ZoneType, String> cardTexts, final Player p, final int landsPlayed, final int landsPlayedLastTurn) {
|
||||||
// Lock check static as we setup player state
|
// Lock check static as we setup player state
|
||||||
|
|
||||||
Map<ZoneType, CardCollectionView> playerCards = new EnumMap<ZoneType, CardCollectionView>(ZoneType.class);
|
Map<ZoneType, CardCollectionView> playerCards = new EnumMap<ZoneType, CardCollectionView>(ZoneType.class);
|
||||||
@@ -885,6 +1011,9 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (life >= 0) p.setLife(life, null);
|
if (life >= 0) p.setLife(life, null);
|
||||||
|
p.setLandsPlayedThisTurn(landsPlayed);
|
||||||
|
p.setLandsPlayedLastTurn(landsPlayedLastTurn);
|
||||||
|
|
||||||
for (Entry<ZoneType, CardCollectionView> kv : playerCards.entrySet()) {
|
for (Entry<ZoneType, CardCollectionView> kv : playerCards.entrySet()) {
|
||||||
PlayerZone zone = p.getZone(kv.getKey());
|
PlayerZone zone = p.getZone(kv.getKey());
|
||||||
if (kv.getKey() == ZoneType.Battlefield) {
|
if (kv.getKey() == ZoneType.Battlefield) {
|
||||||
@@ -1008,6 +1137,10 @@ public abstract class GameState {
|
|||||||
} else if (info.startsWith("Attaching:")) {
|
} else if (info.startsWith("Attaching:")) {
|
||||||
int id = Integer.parseInt(info.substring(info.indexOf(':') + 1));
|
int id = Integer.parseInt(info.substring(info.indexOf(':') + 1));
|
||||||
cardToAttachId.put(c, id);
|
cardToAttachId.put(c, id);
|
||||||
|
} else if (info.startsWith("EnchantingPlayer:")) {
|
||||||
|
// TODO: improve this for game states with more than two players
|
||||||
|
String tgt = info.substring(info.indexOf(':') + 1);
|
||||||
|
cardToEnchantPlayerId.put(c, tgt.equalsIgnoreCase("AI") ? TARGET_AI : TARGET_HUMAN);
|
||||||
} else if (info.startsWith("Ability:")) {
|
} else if (info.startsWith("Ability:")) {
|
||||||
String abString = info.substring(info.indexOf(':') + 1).toLowerCase();
|
String abString = info.substring(info.indexOf(':') + 1).toLowerCase();
|
||||||
c.addSpellAbility(AbilityFactory.getAbility(abilityString.get(abString), c));
|
c.addSpellAbility(AbilityFactory.getAbility(abilityString.get(abString), c));
|
||||||
|
|||||||
@@ -168,12 +168,13 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title) {
|
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title,
|
||||||
|
Map<String, Object> params) {
|
||||||
ApiType api = sa.getApi();
|
ApiType api = sa.getApi();
|
||||||
if (null == api) {
|
if (null == api) {
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
||||||
}
|
}
|
||||||
return SpellApiToAi.Converter.get(api).chooseSingleSpellAbility(player, sa, spells);
|
return SpellApiToAi.Converter.get(api).chooseSingleSpellAbility(player, sa, spells, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -291,9 +292,38 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
return ImmutablePair.of(toTop, toBottom);
|
return ImmutablePair.of(toTop, toBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.game.player.PlayerController#arrangeForSurveil(forge.game.card.CardCollection)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ImmutablePair<CardCollection, CardCollection> arrangeForSurveil(CardCollection topN) {
|
||||||
|
CardCollection toGraveyard = new CardCollection();
|
||||||
|
CardCollection toTop = new CardCollection();
|
||||||
|
|
||||||
|
// TODO: Currently this logic uses the same routine as Scry. Possibly differentiate this and implement
|
||||||
|
// a specific logic for Surveil (e.g. maybe to interact better with Reanimator strategies etc.).
|
||||||
|
if (getPlayer().getCardsIn(ZoneType.Hand).size() <= getAi().getIntProperty(AiProps.SURVEIL_NUM_CARDS_IN_LIBRARY_TO_BAIL)) {
|
||||||
|
toTop.addAll(topN);
|
||||||
|
} else {
|
||||||
|
for (Card c : topN) {
|
||||||
|
if (ComputerUtil.scryWillMoveCardToBottomOfLibrary(player, c)) {
|
||||||
|
toGraveyard.add(c);
|
||||||
|
} else {
|
||||||
|
toTop.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.shuffle(toTop, MyRandom.getRandom());
|
||||||
|
return ImmutablePair.of(toTop, toGraveyard);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean willPutCardOnTop(Card c) {
|
public boolean willPutCardOnTop(Card c) {
|
||||||
return true; // AI does not know what will happen next (another clash or that would become his topdeck)
|
// This is used for Clash. Currently uses Scry logic to determine whether the card should be put on top.
|
||||||
|
// Note that the AI does not know what will happen next (another clash or that would become his topdeck)
|
||||||
|
|
||||||
|
return !ComputerUtil.scryWillMoveCardToBottomOfLibrary(player, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ public abstract class SpellAbilityAi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells, Map<String, Object> params) {
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleSpellAbility is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleSpellAbility is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
return spells.get(0);
|
return spells.get(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public enum SpellApiToAi {
|
|||||||
.put(ApiType.AnimateAll, AnimateAllAi.class)
|
.put(ApiType.AnimateAll, AnimateAllAi.class)
|
||||||
.put(ApiType.Attach, AttachAi.class)
|
.put(ApiType.Attach, AttachAi.class)
|
||||||
.put(ApiType.Ascend, AlwaysPlayAi.class)
|
.put(ApiType.Ascend, AlwaysPlayAi.class)
|
||||||
|
.put(ApiType.AssignGroup, AssignGroupAi.class)
|
||||||
.put(ApiType.Balance, BalanceAi.class)
|
.put(ApiType.Balance, BalanceAi.class)
|
||||||
.put(ApiType.BecomeMonarch, AlwaysPlayAi.class)
|
.put(ApiType.BecomeMonarch, AlwaysPlayAi.class)
|
||||||
.put(ApiType.BecomesBlocked, BecomesBlockedAi.class)
|
.put(ApiType.BecomesBlocked, BecomesBlockedAi.class)
|
||||||
@@ -144,6 +145,7 @@ public enum SpellApiToAi {
|
|||||||
.put(ApiType.SkipTurn, SkipTurnAi.class)
|
.put(ApiType.SkipTurn, SkipTurnAi.class)
|
||||||
.put(ApiType.StoreMap, StoreMapAi.class)
|
.put(ApiType.StoreMap, StoreMapAi.class)
|
||||||
.put(ApiType.StoreSVar, StoreSVarAi.class)
|
.put(ApiType.StoreSVar, StoreSVarAi.class)
|
||||||
|
.put(ApiType.Surveil, SurveilAi.class)
|
||||||
.put(ApiType.Tap, TapAi.class)
|
.put(ApiType.Tap, TapAi.class)
|
||||||
.put(ApiType.TapAll, TapAllAi.class)
|
.put(ApiType.TapAll, TapAllAi.class)
|
||||||
.put(ApiType.TapOrUntap, TapOrUntapAi.class)
|
.put(ApiType.TapOrUntap, TapOrUntapAi.class)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import forge.game.zone.ZoneType;
|
|||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class ActivateAbilityAi extends SpellAbilityAi {
|
public class ActivateAbilityAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@@ -93,7 +94,8 @@ public class ActivateAbilityAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells,
|
||||||
|
Map<String, Object> params) {
|
||||||
return spells.get(0);
|
return spells.get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ import forge.game.staticability.StaticAbilityLayer;
|
|||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerHandler;
|
import forge.game.trigger.TriggerHandler;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.collect.FCollectionView;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import forge.game.ability.effects.AnimateEffectBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -363,11 +363,11 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
card.setSickness(hasOriginalCardSickness);
|
card.setSickness(hasOriginalCardSickness);
|
||||||
|
|
||||||
// AF specific sa
|
// AF specific sa
|
||||||
int power = -1;
|
Integer power = null;
|
||||||
if (sa.hasParam("Power")) {
|
if (sa.hasParam("Power")) {
|
||||||
power = AbilityUtils.calculateAmount(source, sa.getParam("Power"), sa);
|
power = AbilityUtils.calculateAmount(source, sa.getParam("Power"), sa);
|
||||||
}
|
}
|
||||||
int toughness = -1;
|
Integer toughness = null;
|
||||||
if (sa.hasParam("Toughness")) {
|
if (sa.hasParam("Toughness")) {
|
||||||
toughness = AbilityUtils.calculateAmount(source, sa.getParam("Toughness"), sa);
|
toughness = AbilityUtils.calculateAmount(source, sa.getParam("Toughness"), sa);
|
||||||
}
|
}
|
||||||
@@ -453,65 +453,7 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
sVars.addAll(Arrays.asList(sa.getParam("sVars").split(",")));
|
sVars.addAll(Arrays.asList(sa.getParam("sVars").split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// duplicating AnimateEffectBase.doAnimate
|
AnimateEffectBase.doAnimate(card, sa, power, toughness, types, removeTypes, finalDesc, keywords, removeKeywords, hiddenKeywords, timestamp);
|
||||||
boolean removeSuperTypes = false;
|
|
||||||
boolean removeCardTypes = false;
|
|
||||||
boolean removeSubTypes = false;
|
|
||||||
boolean removeCreatureTypes = false;
|
|
||||||
boolean removeArtifactTypes = false;
|
|
||||||
|
|
||||||
if (sa.hasParam("OverwriteTypes")) {
|
|
||||||
removeSuperTypes = true;
|
|
||||||
removeCardTypes = true;
|
|
||||||
removeSubTypes = true;
|
|
||||||
removeCreatureTypes = true;
|
|
||||||
removeArtifactTypes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("KeepSupertypes")) {
|
|
||||||
removeSuperTypes = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("KeepCardTypes")) {
|
|
||||||
removeCardTypes = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("RemoveSuperTypes")) {
|
|
||||||
removeSuperTypes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("RemoveCardTypes")) {
|
|
||||||
removeCardTypes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("RemoveSubTypes")) {
|
|
||||||
removeSubTypes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("RemoveCreatureTypes")) {
|
|
||||||
removeCreatureTypes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("RemoveArtifactTypes")) {
|
|
||||||
removeArtifactTypes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((power != -1) || (toughness != -1)) {
|
|
||||||
card.addNewPT(power, toughness, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!types.isEmpty() || !removeTypes.isEmpty() || removeCreatureTypes) {
|
|
||||||
card.addChangedCardTypes(types, removeTypes, removeSuperTypes, removeCardTypes, removeSubTypes,
|
|
||||||
removeCreatureTypes, removeArtifactTypes, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
card.addChangedCardKeywords(keywords, removeKeywords, sa.hasParam("RemoveAllAbilities"), timestamp);
|
|
||||||
|
|
||||||
for (final String k : hiddenKeywords) {
|
|
||||||
card.addHiddenExtrinsicKeyword(k);
|
|
||||||
}
|
|
||||||
|
|
||||||
card.addColor(finalDesc, !sa.hasParam("OverwriteColors"), timestamp);
|
|
||||||
|
|
||||||
// back to duplicating AnimateEffect.resolve
|
// back to duplicating AnimateEffect.resolve
|
||||||
// TODO will all these abilities/triggers/replacements/etc. lead to
|
// TODO will all these abilities/triggers/replacements/etc. lead to
|
||||||
@@ -521,10 +463,14 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
boolean clearAbilities = sa.hasParam("OverwriteAbilities");
|
boolean clearAbilities = sa.hasParam("OverwriteAbilities");
|
||||||
boolean clearSpells = sa.hasParam("OverwriteSpells");
|
boolean clearSpells = sa.hasParam("OverwriteSpells");
|
||||||
boolean removeAll = sa.hasParam("RemoveAllAbilities");
|
boolean removeAll = sa.hasParam("RemoveAllAbilities");
|
||||||
|
boolean removeIntrinsic = sa.hasParam("RemoveIntrinsicAbilities");
|
||||||
|
|
||||||
if (clearAbilities || clearSpells || removeAll) {
|
if (clearAbilities || clearSpells || removeAll) {
|
||||||
for (final SpellAbility ab : card.getSpellAbilities()) {
|
for (final SpellAbility ab : card.getSpellAbilities()) {
|
||||||
if (removeAll || (ab.isAbility() && clearAbilities) || (ab.isSpell() && clearSpells)) {
|
if (removeAll
|
||||||
|
|| (ab.isIntrinsic() && removeIntrinsic && !ab.isBasicLandAbility())
|
||||||
|
|| (ab.isAbility() && clearAbilities)
|
||||||
|
|| (ab.isSpell() && clearSpells)) {
|
||||||
card.removeSpellAbility(ab);
|
card.removeSpellAbility(ab);
|
||||||
removedAbilities.add(ab);
|
removedAbilities.add(ab);
|
||||||
}
|
}
|
||||||
@@ -565,9 +511,11 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
// suppress triggers from the animated card
|
// suppress triggers from the animated card
|
||||||
final List<Trigger> removedTriggers = Lists.newArrayList();
|
final List<Trigger> removedTriggers = Lists.newArrayList();
|
||||||
if (sa.hasParam("OverwriteTriggers") || removeAll) {
|
if (sa.hasParam("OverwriteTriggers") || removeAll || removeIntrinsic) {
|
||||||
final FCollectionView<Trigger> triggersToRemove = card.getTriggers();
|
for (final Trigger trigger : card.getTriggers()) {
|
||||||
for (final Trigger trigger : triggersToRemove) {
|
if (removeIntrinsic && !trigger.isIntrinsic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
trigger.setSuppressed(true);
|
trigger.setSuppressed(true);
|
||||||
removedTriggers.add(trigger);
|
removedTriggers.add(trigger);
|
||||||
}
|
}
|
||||||
@@ -603,9 +551,11 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
// suppress static abilities from the animated card
|
// suppress static abilities from the animated card
|
||||||
final List<StaticAbility> removedStatics = Lists.newArrayList();
|
final List<StaticAbility> removedStatics = Lists.newArrayList();
|
||||||
if (sa.hasParam("OverwriteStatics") || removeAll) {
|
if (sa.hasParam("OverwriteStatics") || removeAll || removeIntrinsic) {
|
||||||
final FCollectionView<StaticAbility> staticsToRemove = card.getStaticAbilities();
|
for (final StaticAbility stAb : card.getStaticAbilities()) {
|
||||||
for (final StaticAbility stAb : staticsToRemove) {
|
if (removeIntrinsic && !stAb.isIntrinsic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
stAb.setTemporarilySuppressed(true);
|
stAb.setTemporarilySuppressed(true);
|
||||||
removedStatics.add(stAb);
|
removedStatics.add(stAb);
|
||||||
}
|
}
|
||||||
@@ -613,8 +563,11 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
// suppress static abilities from the animated card
|
// suppress static abilities from the animated card
|
||||||
final List<ReplacementEffect> removedReplacements = Lists.newArrayList();
|
final List<ReplacementEffect> removedReplacements = Lists.newArrayList();
|
||||||
if (sa.hasParam("OverwriteReplacements") || removeAll) {
|
if (sa.hasParam("OverwriteReplacements") || removeAll || removeIntrinsic) {
|
||||||
for (final ReplacementEffect re : card.getReplacementEffects()) {
|
for (final ReplacementEffect re : card.getReplacementEffects()) {
|
||||||
|
if (removeIntrinsic && !re.isIntrinsic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
re.setTemporarilySuppressed(true);
|
re.setTemporarilySuppressed(true);
|
||||||
removedReplacements.add(re);
|
removedReplacements.add(re);
|
||||||
}
|
}
|
||||||
|
|||||||
33
forge-ai/src/main/java/forge/ai/ability/AssignGroupAi.java
Normal file
33
forge-ai/src/main/java/forge/ai/ability/AssignGroupAi.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import forge.ai.SpellAbilityAi;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
|
public class AssignGroupAi extends SpellAbilityAi {
|
||||||
|
|
||||||
|
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||||
|
// TODO: Currently this AI relies on the card-specific limiting hints (NeedsToPlay / NeedsToPlayVar),
|
||||||
|
// otherwise the AI considers the card playable.
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells, Map<String, Object> params) {
|
||||||
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
|
if (logic.equals("FriendOrFoe")) {
|
||||||
|
if (params.containsKey("Affected") && spells.size() >= 2) {
|
||||||
|
Player t = (Player) params.get("Affected");
|
||||||
|
return spells.get(player.isOpponentOf(t) ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Iterables.getFirst(spells, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -510,7 +510,7 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
// Prefer "tap to deal damage"
|
// Prefer "tap to deal damage"
|
||||||
// TODO : Skip this one if triggers on combat damage only?
|
// TODO : Skip this one if triggers on combat damage only?
|
||||||
for (SpellAbility sa2 : card.getSpellAbilities()) {
|
for (SpellAbility sa2 : card.getSpellAbilities()) {
|
||||||
if ((sa2.getApi().equals(ApiType.DealDamage))
|
if (ApiType.DealDamage.equals(sa2.getApi())
|
||||||
&& (sa2.getTargetRestrictions().canTgtPlayer())) {
|
&& (sa2.getTargetRestrictions().canTgtPlayer())) {
|
||||||
cardPriority += 300;
|
cardPriority += 300;
|
||||||
}
|
}
|
||||||
@@ -971,8 +971,8 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((affected.contains(stCheck) || affected.contains("AttachedBy"))) {
|
if ((affected.contains(stCheck) || affected.contains("AttachedBy"))) {
|
||||||
totToughness += AbilityUtils.calculateAmount(attachSource, stabMap.get("AddToughness"), sa);
|
totToughness += AbilityUtils.calculateAmount(attachSource, stabMap.get("AddToughness"), stAbility);
|
||||||
totPower += AbilityUtils.calculateAmount(attachSource, stabMap.get("AddPower"), sa);
|
totPower += AbilityUtils.calculateAmount(attachSource, stabMap.get("AddPower"), stAbility);
|
||||||
|
|
||||||
grantingAbilities |= stabMap.containsKey("AddAbility");
|
grantingAbilities |= stabMap.containsKey("AddAbility");
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import forge.game.player.Player;
|
|||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class CanPlayAsDrawbackAi extends SpellAbilityAi {
|
public class CanPlayAsDrawbackAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@@ -37,7 +38,8 @@ public class CanPlayAsDrawbackAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells,
|
||||||
|
Map<String, Object> params) {
|
||||||
// This might be called from CopySpellAbilityEffect - to hide warning (for having no overload) use this simple overload
|
// This might be called from CopySpellAbilityEffect - to hide warning (for having no overload) use this simple overload
|
||||||
return spells.get(0);
|
return spells.get(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,7 +152,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
return doReturnCommanderLogic(sa, aiPlayer);
|
return doReturnCommanderLogic(sa, aiPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("IfNotBuffed".equals(sa.getParam("AILogic"))) {
|
if ("Always".equals(sa.getParam("AILogic"))) {
|
||||||
|
return true;
|
||||||
|
} else if ("IfNotBuffed".equals(sa.getParam("AILogic"))) {
|
||||||
if (ComputerUtilCard.isUselessCreature(aiPlayer, sa.getHostCard())) {
|
if (ComputerUtilCard.isUselessCreature(aiPlayer, sa.getHostCard())) {
|
||||||
return true; // debuffed by opponent's auras to the level that it becomes useless
|
return true; // debuffed by opponent's auras to the level that it becomes useless
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
@@ -79,7 +80,8 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells,
|
||||||
|
Map<String, Object> params) {
|
||||||
Card host = sa.getHostCard();
|
Card host = sa.getHostCard();
|
||||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||||
final Game game = host.getGame();
|
final Game game = host.getGame();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import forge.game.player.PlayerActionConfirmMode;
|
|||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class CopySpellAbilityAi extends SpellAbilityAi {
|
public class CopySpellAbilityAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@@ -36,7 +37,8 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells) {
|
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List<SpellAbility> spells,
|
||||||
|
Map<String, Object> params) {
|
||||||
return spells.get(0);
|
return spells.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
CardCollection list;
|
CardCollection list;
|
||||||
Card choice = null;
|
Card choice = null;
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final String amountStr = sa.getParam("CounterNum");
|
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
||||||
final String logic = sa.getParamOrDefault("AILogic", "");
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||||
@@ -220,13 +220,9 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
if ("Never".equals(logic)) {
|
if ("Never".equals(logic)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
} else if ("PayEnergy".equals(logic)) {
|
||||||
|
|
||||||
if ("PayEnergy".equals(logic)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if ("PayEnergyConservatively".equals(logic)) {
|
||||||
|
|
||||||
if ("PayEnergyConservatively".equals(logic)) {
|
|
||||||
boolean onlyInCombat = ai.getController().isAI()
|
boolean onlyInCombat = ai.getController().isAI()
|
||||||
&& ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT);
|
&& ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT);
|
||||||
boolean onlyDefensive = ai.getController().isAI()
|
boolean onlyDefensive = ai.getController().isAI()
|
||||||
@@ -266,9 +262,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (logic.equals("MarkOppCreature")) {
|
||||||
|
|
||||||
if (logic.equals("MarkOppCreature")) {
|
|
||||||
if (!ph.is(PhaseType.END_OF_TURN)) {
|
if (!ph.is(PhaseType.END_OF_TURN)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -283,6 +277,11 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
sa.getTargets().add(bestCreat);
|
sa.getTargets().add(bestCreat);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if (logic.equals("CheckDFC")) {
|
||||||
|
// for cards like Ludevic's Test Subject
|
||||||
|
if (!source.canTransform()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) {
|
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) {
|
||||||
@@ -581,7 +580,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final String logic = sa.getParamOrDefault("AILogic", "");
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
final String amountStr = sa.getParam("CounterNum");
|
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
||||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
|
|
||||||
@@ -661,7 +660,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
boolean preferred = true;
|
boolean preferred = true;
|
||||||
CardCollection list;
|
CardCollection list;
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final String amountStr = sa.getParam("CounterNum");
|
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
||||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
int left = amount;
|
int left = amount;
|
||||||
@@ -804,7 +803,8 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
if (mode == PlayerActionConfirmMode.Tribute) {
|
if (mode == PlayerActionConfirmMode.Tribute) {
|
||||||
// add counter if that opponent has a giant creature
|
// add counter if that opponent has a giant creature
|
||||||
final List<Card> creats = player.getCreaturesInPlay();
|
final List<Card> creats = player.getCreaturesInPlay();
|
||||||
final int tributeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
|
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||||
|
final int tributeAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
|
|
||||||
final boolean isHaste = source.hasKeyword(Keyword.HASTE);
|
final boolean isHaste = source.hasKeyword(Keyword.HASTE);
|
||||||
List<Card> threatening = CardLists.filter(creats, new Predicate<Card>() {
|
List<Card> threatening = CardLists.filter(creats, new Predicate<Card>() {
|
||||||
@@ -863,7 +863,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
|
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
|
||||||
final String amountStr = sa.getParam("CounterNum");
|
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
|
|
||||||
final boolean isCurse = sa.isCurse();
|
final boolean isCurse = sa.isCurse();
|
||||||
|
|||||||
@@ -481,10 +481,11 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
final PhaseHandler phase = game.getPhaseHandler();
|
final PhaseHandler phase = game.getPhaseHandler();
|
||||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
||||||
final boolean oppTargetsChoice = sa.hasParam("TargetingPlayer");
|
final boolean oppTargetsChoice = sa.hasParam("TargetingPlayer");
|
||||||
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
Player enemy = ComputerUtil.getOpponentFor(ai);
|
Player enemy = ComputerUtil.getOpponentFor(ai);
|
||||||
|
|
||||||
if ("PowerDmg".equals(sa.getParam("AILogic"))) {
|
if ("PowerDmg".equals(logic)) {
|
||||||
// check if it is better to target the player instead, the original target is already set in PumpAi.pumpTgtAI()
|
// check if it is better to target the player instead, the original target is already set in PumpAi.pumpTgtAI()
|
||||||
if (tgt.canTgtCreatureAndPlayer() && this.shouldTgtP(ai, sa, dmg, noPrevention)){
|
if (tgt.canTgtCreatureAndPlayer() && this.shouldTgtP(ai, sa, dmg, noPrevention)){
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
@@ -504,11 +505,11 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
TargetChoices tcs = sa.getTargets();
|
TargetChoices tcs = sa.getTargets();
|
||||||
|
|
||||||
// Do not use if would kill self
|
// Do not use if would kill self
|
||||||
if (("SelfDamage".equals(sa.getParam("AILogic"))) && (ai.getLife() <= Integer.parseInt(source.getSVar("SelfDamageAmount")))) {
|
if (("SelfDamage".equals(logic)) && (ai.getLife() <= Integer.parseInt(source.getSVar("SelfDamageAmount")))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("ChoiceBurn".equals(sa.getParam("AILogic"))) {
|
if ("ChoiceBurn".equals(logic)) {
|
||||||
// do not waste burns on player if other choices are present
|
// do not waste burns on player if other choices are present
|
||||||
if (this.shouldTgtP(ai, sa, dmg, noPrevention)) {
|
if (this.shouldTgtP(ai, sa, dmg, noPrevention)) {
|
||||||
tcs.add(enemy);
|
tcs.add(enemy);
|
||||||
@@ -517,7 +518,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ("Polukranos".equals(sa.getParam("AILogic"))) {
|
if ("Polukranos".equals(logic)) {
|
||||||
int dmgTaken = 0;
|
int dmgTaken = 0;
|
||||||
CardCollection humCreatures = enemy.getCreaturesInPlay();
|
CardCollection humCreatures = enemy.getCreaturesInPlay();
|
||||||
Card lastTgt = null;
|
Card lastTgt = null;
|
||||||
@@ -681,7 +682,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if ("OppAtTenLife".equals(sa.getParam("AILogic"))) {
|
} else if ("OppAtTenLife".equals(logic)) {
|
||||||
for (final Player p : ai.getOpponents()) {
|
for (final Player p : ai.getOpponents()) {
|
||||||
if (sa.canTarget(p) && p.getLife() == 10 && tcs.getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
if (sa.canTarget(p) && p.getLife() == 10 && tcs.getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
||||||
tcs.add(p);
|
tcs.add(p);
|
||||||
@@ -690,9 +691,10 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
}
|
}
|
||||||
// TODO: Improve Damage, we shouldn't just target the player just
|
// TODO: Improve Damage, we shouldn't just target the player just
|
||||||
// because we can
|
// because we can
|
||||||
else if (sa.canTarget(enemy)) {
|
if (sa.canTarget(enemy) && tcs.getNumTargeted() < tgt.getMaxTargets(source, sa)) {
|
||||||
if (((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai))
|
if (((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai))
|
||||||
|| (SpellAbilityAi.isSorcerySpeed(sa) && phase.is(PhaseType.MAIN2))
|
|| (SpellAbilityAi.isSorcerySpeed(sa) && phase.is(PhaseType.MAIN2))
|
||||||
|
|| ("PingAfterAttack".equals(logic) && phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai))
|
||||||
|| sa.getPayCosts() == null || immediately
|
|| sa.getPayCosts() == null || immediately
|
||||||
|| this.shouldTgtP(ai, sa, dmg, noPrevention)) &&
|
|| this.shouldTgtP(ai, sa, dmg, noPrevention)) &&
|
||||||
(!avoidTargetP(ai, sa))) {
|
(!avoidTargetP(ai, sa))) {
|
||||||
|
|||||||
@@ -26,10 +26,7 @@ import forge.game.card.Card;
|
|||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.*;
|
||||||
import forge.game.cost.CostDiscard;
|
|
||||||
import forge.game.cost.CostPart;
|
|
||||||
import forge.game.cost.PaymentDecision;
|
|
||||||
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;
|
||||||
@@ -252,19 +249,34 @@ public class DrawAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (num != null && num.equals("ChosenX")) {
|
if (num != null && num.equals("ChosenX")) {
|
||||||
// Necrologia, Pay X Life : Draw X Cards
|
|
||||||
if (sa.getSVar("X").equals("XChoice")) {
|
if (sa.getSVar("X").equals("XChoice")) {
|
||||||
// Draw up to max hand size but leave at least 3 in library
|
// Draw up to max hand size but leave at least 3 in library
|
||||||
numCards = Math.min(computerMaxHandSize - computerHandSize, computerLibrarySize - 3);
|
numCards = Math.min(computerMaxHandSize - computerHandSize, computerLibrarySize - 3);
|
||||||
// But no more than what's "safe" and doesn't risk a near death experience
|
|
||||||
|
if (sa.getPayCosts() != null) {
|
||||||
|
if (sa.getPayCosts().hasSpecificCostType(CostPayLife.class)) {
|
||||||
|
// [Necrologia, Pay X Life : Draw X Cards]
|
||||||
|
// Don't draw more than what's "safe" and don't risk a near death experience
|
||||||
// Maybe would be better to check for "serious danger" and take more risk?
|
// Maybe would be better to check for "serious danger" and take more risk?
|
||||||
while ((ComputerUtil.aiLifeInDanger(ai, false, numCards) && (numCards > 0))) {
|
while ((ComputerUtil.aiLifeInDanger(ai, false, numCards) && (numCards > 0))) {
|
||||||
numCards--;
|
numCards--;
|
||||||
}
|
}
|
||||||
|
} else if (sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) {
|
||||||
|
// [e.g. Krav, the Unredeemed and other cases which say "Sacrifice X creatures: draw X cards]
|
||||||
|
// TODO: Add special logic to limit/otherwise modify the ChosenX value here
|
||||||
|
|
||||||
|
// Skip this ability if nothing is to be chosen for sacrifice
|
||||||
|
if (numCards <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sa.setSVar("ChosenX", Integer.toString(numCards));
|
sa.setSVar("ChosenX", Integer.toString(numCards));
|
||||||
source.setSVar("ChosenX", Integer.toString(numCards));
|
source.setSVar("ChosenX", Integer.toString(numCards));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logic for cards that require special handling
|
// Logic for cards that require special handling
|
||||||
if ("YawgmothsBargain".equals(logic)) {
|
if ("YawgmothsBargain".equals(logic)) {
|
||||||
return SpecialCardAi.YawgmothsBargain.consider(ai, sa);
|
return SpecialCardAi.YawgmothsBargain.consider(ai, sa);
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ public class FightAi extends SpellAbilityAi {
|
|||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
|
// everything is defined or targeted above, can't do anything there?
|
||||||
|
if (sa.hasParam("Defined") && !sa.usesTargeting()) {
|
||||||
|
// TODO extend Logic for cards like Arena or Grothama
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Get creature lists
|
// Get creature lists
|
||||||
CardCollectionView aiCreatures = ai.getCreaturesInPlay();
|
CardCollectionView aiCreatures = ai.getCreaturesInPlay();
|
||||||
aiCreatures = CardLists.getTargetableCards(aiCreatures, sa);
|
aiCreatures = CardLists.getTargetableCards(aiCreatures, sa);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import forge.game.card.Card;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
|
import forge.game.zone.MagicStack;
|
||||||
|
|
||||||
public class LifeExchangeVariantAi extends SpellAbilityAi {
|
public class LifeExchangeVariantAi extends SpellAbilityAi {
|
||||||
|
|
||||||
@@ -83,7 +84,25 @@ public class LifeExchangeVariantAi extends SpellAbilityAi {
|
|||||||
return shouldDo;
|
return shouldDo;
|
||||||
}
|
}
|
||||||
else if ("Evra, Halcyon Witness".equals(sourceName)) {
|
else if ("Evra, Halcyon Witness".equals(sourceName)) {
|
||||||
// TODO add logic
|
if (!ai.canGainLife())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int aiLife = ai.getLife();
|
||||||
|
|
||||||
|
if (source.getNetPower() > aiLife) {
|
||||||
|
if (ComputerUtilCombat.lifeInSeriousDanger(ai, ai.getGame().getCombat())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the top of stack
|
||||||
|
MagicStack stack = ai.getGame().getStack();
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
SpellAbility saTop = stack.peekAbility();
|
||||||
|
if (ComputerUtil.predictDamageFromSpell(saTop, ai) >= aiLife) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import forge.game.phase.PhaseType;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
@@ -22,9 +21,8 @@ public class ScryAi extends SpellAbilityAi {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
|
||||||
|
|
||||||
if (tgt != null) { // It doesn't appear that Scry ever targets
|
if (sa.usesTargeting()) { // It doesn't appear that Scry ever targets
|
||||||
// ability is targeted
|
// ability is targeted
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package forge.ai.ability;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.card.CardSplitType;
|
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GlobalRuleChange;
|
import forge.game.GlobalRuleChange;
|
||||||
@@ -65,8 +64,6 @@ public class SetStateAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean checkAiLogic(final Player aiPlayer, final SpellAbility sa, final String aiLogic) {
|
protected boolean checkAiLogic(final Player aiPlayer, final SpellAbility sa, final String aiLogic) {
|
||||||
final Card source = sa.getHostCard();
|
|
||||||
|
|
||||||
return super.checkAiLogic(aiPlayer, sa, aiLogic);
|
return super.checkAiLogic(aiPlayer, sa, aiLogic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +84,7 @@ public class SetStateAi extends SpellAbilityAi {
|
|||||||
if("Transform".equals(mode)) {
|
if("Transform".equals(mode)) {
|
||||||
if (!sa.usesTargeting()) {
|
if (!sa.usesTargeting()) {
|
||||||
// no Transform with Defined which is not Self
|
// no Transform with Defined which is not Self
|
||||||
if (source.hasKeyword("CARDNAME can't transform")) {
|
if (!source.canTransform()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return shouldTransformCard(source, ai, ph) || "Always".equals(logic);
|
return shouldTransformCard(source, ai, ph) || "Always".equals(logic);
|
||||||
@@ -96,15 +93,13 @@ public class SetStateAi extends SpellAbilityAi {
|
|||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
|
||||||
CardCollection list = CardLists.getValidCards(CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES), tgt.getValidTgts(), ai, source, sa);
|
CardCollection list = CardLists.getValidCards(CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES), tgt.getValidTgts(), ai, source, sa);
|
||||||
// select only cards with Transform as SplitType
|
// select only the ones that can transform
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Card c) {
|
public boolean apply(Card c) {
|
||||||
return c.hasAlternateState() && c.getRules().getSplitType() == CardSplitType.Transform;
|
return c.canTransform();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// select only the ones that can transform
|
|
||||||
list = CardLists.getNotKeyword(list, "CARDNAME can't transform");
|
|
||||||
list = CardLists.getTargetableCards(list, sa);
|
list = CardLists.getTargetableCards(list, sa);
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
|
|||||||
104
forge-ai/src/main/java/forge/ai/ability/SurveilAi.java
Normal file
104
forge-ai/src/main/java/forge/ai/ability/SurveilAi.java
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import forge.ai.SpellAbilityAi;
|
||||||
|
import forge.game.phase.PhaseHandler;
|
||||||
|
import forge.game.phase.PhaseType;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.util.MyRandom;
|
||||||
|
|
||||||
|
public class SurveilAi extends SpellAbilityAi {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see forge.ai.SpellAbilityAi#doTriggerAINoCost(forge.game.player.Player, forge.game.spellability.SpellAbility, boolean)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
|
||||||
|
if (sa.usesTargeting()) { // TODO: It doesn't appear that Surveil ever targets, is this necessary?
|
||||||
|
sa.resetTargets();
|
||||||
|
sa.getTargets().add(ai);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see forge.ai.SpellAbilityAi#chkAIDrawback(forge.game.spellability.SpellAbility, forge.game.player.Player)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
|
return doTriggerAINoCost(ai, sa, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the AI will play a SpellAbility based on its phase restrictions
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
|
||||||
|
// if the Surveil ability requires tapping and has a mana cost, it's best done at the end of opponent's turn
|
||||||
|
// and right before the beginning of AI's turn, if possible, to avoid mana locking the AI and also to
|
||||||
|
// try to scry right before drawing a card. Also, avoid tapping creatures in the AI's turn, if possible,
|
||||||
|
// even if there's no mana cost.
|
||||||
|
if (sa.getPayCosts() != null) {
|
||||||
|
if (sa.getPayCosts().hasTapCost()
|
||||||
|
&& (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature()))
|
||||||
|
&& !SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
|
return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// in the player's turn Surveil should only be done in Main1 or in Upkeep if able
|
||||||
|
if (ph.isPlayerTurn(ai)) {
|
||||||
|
if (SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
|
return ph.is(PhaseType.MAIN1) || sa.hasParam("Planeswalker");
|
||||||
|
} else {
|
||||||
|
return ph.is(PhaseType.UPKEEP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the AI will play a SpellAbility with the specified AiLogic
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
|
||||||
|
if ("Never".equals(aiLogic)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add card-specific Surveil AI logic here when/if necessary
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
|
// Makes no sense to do Surveil when there's nothing in the library
|
||||||
|
if (ai.getCardsIn(ZoneType.Library).isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double chance = .4; // 40 percent chance for instant speed
|
||||||
|
if (SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
|
chance = .667; // 66.7% chance for sorcery speed (since it will never activate EOT)
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
|
||||||
|
if (SpellAbilityAi.playReusable(ai, sa)) {
|
||||||
|
randomReturn = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return randomReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import forge.game.GameEntity;
|
|||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
|
import forge.game.ability.effects.TokenEffect;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.card.token.TokenInfo;
|
import forge.game.card.token.TokenInfo;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
@@ -42,14 +43,11 @@ import java.util.List;
|
|||||||
* @version $Id: AbilityFactoryToken.java 17656 2012-10-22 19:32:56Z Max mtg $
|
* @version $Id: AbilityFactoryToken.java 17656 2012-10-22 19:32:56Z Max mtg $
|
||||||
*/
|
*/
|
||||||
public class TokenAi extends SpellAbilityAi {
|
public class TokenAi extends SpellAbilityAi {
|
||||||
|
|
||||||
|
|
||||||
private String tokenAmount;
|
private String tokenAmount;
|
||||||
private String tokenName;
|
|
||||||
private String[] tokenTypes;
|
|
||||||
private String[] tokenKeywords;
|
|
||||||
private String tokenPower;
|
private String tokenPower;
|
||||||
private String tokenToughness;
|
private String tokenToughness;
|
||||||
|
|
||||||
|
private Card actualToken;
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Constructor for AbilityFactory_Token.
|
* Constructor for AbilityFactory_Token.
|
||||||
@@ -58,6 +56,13 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
* a {@link forge.game.ability.AbilityFactory} object.
|
* a {@link forge.game.ability.AbilityFactory} object.
|
||||||
*/
|
*/
|
||||||
private void readParameters(final SpellAbility mapParams) {
|
private void readParameters(final SpellAbility mapParams) {
|
||||||
|
this.tokenAmount = mapParams.getParamOrDefault("TokenAmount", "1");
|
||||||
|
|
||||||
|
TokenEffect effect = new TokenEffect();
|
||||||
|
|
||||||
|
this.actualToken = effect.loadTokenPrototype(mapParams);
|
||||||
|
|
||||||
|
if (actualToken == null) {
|
||||||
String[] keywords;
|
String[] keywords;
|
||||||
|
|
||||||
if (mapParams.hasParam("TokenKeywords")) {
|
if (mapParams.hasParam("TokenKeywords")) {
|
||||||
@@ -67,14 +72,12 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
keywords = new String[0];
|
keywords = new String[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.tokenAmount = mapParams.getParamOrDefault("TokenAmount", "1");
|
|
||||||
this.tokenPower = mapParams.getParam("TokenPower");
|
this.tokenPower = mapParams.getParam("TokenPower");
|
||||||
this.tokenToughness = mapParams.getParam("TokenToughness");
|
this.tokenToughness = mapParams.getParam("TokenToughness");
|
||||||
this.tokenName = mapParams.getParam("TokenName");
|
} else {
|
||||||
this.tokenTypes = mapParams.getParam("TokenTypes").split(",");
|
this.tokenPower = actualToken.getBasePowerString();
|
||||||
this.tokenKeywords = keywords;
|
this.tokenToughness = actualToken.getBaseToughnessString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -103,8 +106,11 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Card token = spawnToken(ai, sa);
|
if (actualToken == null) {
|
||||||
if (token == null) {
|
actualToken = spawnToken(ai, sa);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actualToken == null) {
|
||||||
final AbilitySub sub = sa.getSubAbility();
|
final AbilitySub sub = sa.getSubAbility();
|
||||||
if (pwPlus || (sub != null && SpellApiToAi.Converter.get(sub.getApi()).chkAIDrawback(sub, ai))) {
|
if (pwPlus || (sub != null && SpellApiToAi.Converter.get(sub.getApi()).chkAIDrawback(sub, ai))) {
|
||||||
return true; // planeswalker plus ability or sub-ability is
|
return true; // planeswalker plus ability or sub-ability is
|
||||||
@@ -130,24 +136,21 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canInterruptSacrifice(ai, sa, token)) {
|
if (canInterruptSacrifice(ai, sa, actualToken)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean haste = false;
|
boolean haste = this.actualToken.hasKeyword(Keyword.HASTE);
|
||||||
boolean oneShot = sa.getSubAbility() != null
|
boolean oneShot = sa.getSubAbility() != null
|
||||||
&& sa.getSubAbility().getApi() == ApiType.DelayedTrigger;
|
&& sa.getSubAbility().getApi() == ApiType.DelayedTrigger;
|
||||||
for (final String kw : this.tokenKeywords) {
|
boolean isCreature = this.actualToken.getType().isCreature();
|
||||||
if (kw.equals("Haste")) {
|
|
||||||
haste = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Don't generate tokens without haste before main 2 if possible
|
// Don't generate tokens without haste before main 2 if possible
|
||||||
if (ph.getPhase().isBefore(PhaseType.MAIN2) && ph.isPlayerTurn(ai) && !haste && !sa.hasParam("ActivationPhases")
|
if (ph.getPhase().isBefore(PhaseType.MAIN2) && ph.isPlayerTurn(ai) && !haste && !sa.hasParam("ActivationPhases")
|
||||||
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
&& !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||||
boolean buff = false;
|
boolean buff = false;
|
||||||
for (Card c : ai.getCardsIn(ZoneType.Battlefield)) {
|
for (Card c : ai.getCardsIn(ZoneType.Battlefield)) {
|
||||||
if ("Creature".equals(c.getSVar("BuffedBy"))) {
|
if (isCreature && "Creature".equals(c.getSVar("BuffedBy"))) {
|
||||||
buff = true;
|
buff = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,13 +183,10 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't kill AIs Legendary tokens
|
// Don't kill AIs Legendary tokens
|
||||||
for (final String type : this.tokenTypes) {
|
if (this.actualToken.getType().isLegendary() && ai.isCardInPlay(this.actualToken.getName())) {
|
||||||
if (type.equals("Legendary")) {
|
// TODO Check if Token is useless due to an aura or counters?
|
||||||
if (ai.isCardInPlay(this.tokenName)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
@@ -311,6 +311,18 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mandatory) {
|
||||||
|
// Necessary because the AI goes into this method twice, first to set up targets (with mandatory=true)
|
||||||
|
// and then the second time to confirm the trigger (where mandatory may be set to false).
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("OnlyOnAlliedAttack".equals(sa.getParam("AILogic"))) {
|
||||||
|
Combat combat = ai.getGame().getCombat();
|
||||||
|
return combat != null && combat.getAttackingPlayer() != null
|
||||||
|
&& !combat.getAttackingPlayer().isOpponentOf(ai);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
@@ -388,6 +400,7 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
* @param notNull if the token would not survive, still return it
|
* @param notNull if the token would not survive, still return it
|
||||||
* @return token creature created by ability
|
* @return token creature created by ability
|
||||||
*/
|
*/
|
||||||
|
// TODO Is this just completely copied from TokenEffect? Let's just call that thing
|
||||||
public static Card spawnToken(Player ai, SpellAbility sa, boolean notNull) {
|
public static Card spawnToken(Player ai, SpellAbility sa, boolean notNull) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
|
||||||
@@ -511,7 +524,7 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
// Apply static abilities and prune dead tokens
|
// Apply static abilities and prune dead tokens
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
ComputerUtilCard.applyStaticContPT(game, token, null);
|
ComputerUtilCard.applyStaticContPT(game, token, null);
|
||||||
if (!notNull && token.getNetToughness() < 1) {
|
if (!notNull && token.isCreature() && token.getNetToughness() < 1) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return token;
|
return token;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
||||||
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
|
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.11-SNAPSHOT</version>
|
<version>1.6.16-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-core</artifactId>
|
<artifactId>forge-core</artifactId>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
<version>24.1-jre</version>
|
<version>24.1-android</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
@@ -24,4 +24,31 @@
|
|||||||
<version>3.7</version>
|
<version>3.7</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>checkstyle-validation</id>
|
||||||
|
<phase>validate</phase>
|
||||||
|
<configuration>
|
||||||
|
<configLocation>../checkstyle.xml</configLocation>
|
||||||
|
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<consoleOutput>true</consoleOutput>
|
||||||
|
<failsOnError>true</failsOnError>
|
||||||
|
<failOnViolation>true</failOnViolation>
|
||||||
|
</configuration>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -413,6 +413,9 @@ public class CardStorageReader {
|
|||||||
return reader.readCard(lines, Files.getNameWithoutExtension(file.getName()));
|
return reader.readCard(lines, Files.getNameWithoutExtension(file.getName()));
|
||||||
} catch (final FileNotFoundException ex) {
|
} catch (final FileNotFoundException ex) {
|
||||||
throw new RuntimeException("CardReader : run error -- file not found: " + file.getPath(), ex);
|
throw new RuntimeException("CardReader : run error -- file not found: " + file.getPath(), ex);
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
System.out.println("Error loading cardscript " + file.getName() + ". Please close Forge and resolve this.");
|
||||||
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
assert fileInputStream != null;
|
assert fileInputStream != null;
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class StaticData {
|
|||||||
private Predicate<PaperCard> brawlPredicate;
|
private Predicate<PaperCard> brawlPredicate;
|
||||||
private Predicate<PaperCard> modernPredicate;
|
private Predicate<PaperCard> modernPredicate;
|
||||||
|
|
||||||
|
private boolean filteredHandsEnabled = false;
|
||||||
|
|
||||||
// Loaded lazily:
|
// Loaded lazily:
|
||||||
private IStorage<SealedProduct.Template> boosters;
|
private IStorage<SealedProduct.Template> boosters;
|
||||||
private IStorage<SealedProduct.Template> specialBoosters;
|
private IStorage<SealedProduct.Template> specialBoosters;
|
||||||
@@ -209,6 +211,14 @@ public class StaticData {
|
|||||||
return brawlPredicate;
|
return brawlPredicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFilteredHandsEnabled(boolean filteredHandsEnabled){
|
||||||
|
this.filteredHandsEnabled = filteredHandsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getFilteredHandsEnabled(){
|
||||||
|
return filteredHandsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
public PaperCard getCardByEditionDate(PaperCard card, Date editionDate) {
|
public PaperCard getCardByEditionDate(PaperCard card, Date editionDate) {
|
||||||
|
|
||||||
PaperCard c = this.getCommonCards().getCardFromEdition(card.getName(), editionDate, CardDb.SetPreference.LatestCoreExp, card.getArtIndex());
|
PaperCard c = this.getCommonCards().getCardFromEdition(card.getName(), editionDate, CardDb.SetPreference.LatestCoreExp, card.getArtIndex());
|
||||||
|
|||||||
@@ -32,19 +32,24 @@ public class CardChangedType {
|
|||||||
private final boolean removeSuperTypes;
|
private final boolean removeSuperTypes;
|
||||||
private final boolean removeCardTypes;
|
private final boolean removeCardTypes;
|
||||||
private final boolean removeSubTypes;
|
private final boolean removeSubTypes;
|
||||||
|
private final boolean removeLandTypes;
|
||||||
private final boolean removeCreatureTypes;
|
private final boolean removeCreatureTypes;
|
||||||
private final boolean removeArtifactTypes;
|
private final boolean removeArtifactTypes;
|
||||||
|
private final boolean removeEnchantmentTypes;
|
||||||
|
|
||||||
public CardChangedType(final CardType addType0, final CardType removeType0, final boolean removeSuperType0,
|
public CardChangedType(final CardType addType0, final CardType removeType0, final boolean removeSuperType0,
|
||||||
final boolean removeCardType0, final boolean removeSubType0, final boolean removeCreatureType0,
|
final boolean removeCardType0, final boolean removeSubType0, final boolean removeLandType0,
|
||||||
final boolean removeArtifactType0) {
|
final boolean removeCreatureType0, final boolean removeArtifactType0,
|
||||||
|
final boolean removeEnchantmentTypes0) {
|
||||||
addType = addType0;
|
addType = addType0;
|
||||||
removeType = removeType0;
|
removeType = removeType0;
|
||||||
removeSuperTypes = removeSuperType0;
|
removeSuperTypes = removeSuperType0;
|
||||||
removeCardTypes = removeCardType0;
|
removeCardTypes = removeCardType0;
|
||||||
removeSubTypes = removeSubType0;
|
removeSubTypes = removeSubType0;
|
||||||
|
removeLandTypes = removeLandType0;
|
||||||
removeCreatureTypes = removeCreatureType0;
|
removeCreatureTypes = removeCreatureType0;
|
||||||
removeArtifactTypes = removeArtifactType0;
|
removeArtifactTypes = removeArtifactType0;
|
||||||
|
removeEnchantmentTypes = removeEnchantmentTypes0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final CardType getAddType() {
|
public final CardType getAddType() {
|
||||||
@@ -67,6 +72,10 @@ public class CardChangedType {
|
|||||||
return removeSubTypes;
|
return removeSubTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean isRemoveLandTypes() {
|
||||||
|
return removeLandTypes;
|
||||||
|
}
|
||||||
|
|
||||||
public final boolean isRemoveCreatureTypes() {
|
public final boolean isRemoveCreatureTypes() {
|
||||||
return removeCreatureTypes;
|
return removeCreatureTypes;
|
||||||
}
|
}
|
||||||
@@ -74,4 +83,8 @@ public class CardChangedType {
|
|||||||
public final boolean isRemoveArtifactTypes() {
|
public final boolean isRemoveArtifactTypes() {
|
||||||
return removeArtifactTypes;
|
return removeArtifactTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean isRemoveEnchantmentTypes() {
|
||||||
|
return removeEnchantmentTypes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,12 +123,19 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
private boolean smallSetOverride = false;
|
private boolean smallSetOverride = false;
|
||||||
private String boosterMustContain = "";
|
private String boosterMustContain = "";
|
||||||
private final CardInSet[] cards;
|
private final CardInSet[] cards;
|
||||||
|
private final String[] tokenNormalized;
|
||||||
|
|
||||||
private int boosterArts = 1;
|
private int boosterArts = 1;
|
||||||
private SealedProduct.Template boosterTpl = null;
|
private SealedProduct.Template boosterTpl = null;
|
||||||
|
|
||||||
private CardEdition(CardInSet[] cards) {
|
private CardEdition(CardInSet[] cards) {
|
||||||
this.cards = cards;
|
this.cards = cards;
|
||||||
|
tokenNormalized = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardEdition(CardInSet[] cards, String[] tokens) {
|
||||||
|
this.cards = cards;
|
||||||
|
this.tokenNormalized = tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -254,6 +261,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
protected CardEdition read(File file) {
|
protected CardEdition read(File file) {
|
||||||
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
|
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
|
||||||
|
|
||||||
|
List<String> tokenNormalized = new ArrayList<>();
|
||||||
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
|
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
|
||||||
if (contents.containsKey("cards")) {
|
if (contents.containsKey("cards")) {
|
||||||
for(String line : contents.get("cards")) {
|
for(String line : contents.get("cards")) {
|
||||||
@@ -277,7 +285,19 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CardEdition res = new CardEdition(processedCards.toArray(new CardInSet[processedCards.size()]));
|
if (contents.containsKey("tokens")) {
|
||||||
|
for(String line : contents.get("tokens")) {
|
||||||
|
if (StringUtils.isBlank(line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tokenNormalized.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CardEdition res = new CardEdition(
|
||||||
|
processedCards.toArray(new CardInSet[processedCards.size()]),
|
||||||
|
tokenNormalized.toArray(new String[tokenNormalized.size()])
|
||||||
|
);
|
||||||
|
|
||||||
FileSection section = FileSection.parse(contents.get("metadata"), "=");
|
FileSection section = FileSection.parse(contents.get("metadata"), "=");
|
||||||
res.name = section.get("name");
|
res.name = section.get("name");
|
||||||
|
|||||||
@@ -87,21 +87,27 @@ final class CardFace implements ICardFace {
|
|||||||
void setInitialLoyalty(int value) { this.initialLoyalty = value; }
|
void setInitialLoyalty(int value) { this.initialLoyalty = value; }
|
||||||
|
|
||||||
void setPtText(String value) {
|
void setPtText(String value) {
|
||||||
final int slashPos = value.indexOf('/');
|
final String k[] = value.split("/");
|
||||||
if (slashPos == -1) {
|
|
||||||
|
if (k.length != 2) {
|
||||||
throw new RuntimeException("Creature '" + this.getName() + "' has bad p/t stats");
|
throw new RuntimeException("Creature '" + this.getName() + "' has bad p/t stats");
|
||||||
}
|
}
|
||||||
boolean negPower = value.charAt(0) == '-';
|
|
||||||
boolean negToughness = value.charAt(slashPos + 1) == '-';
|
|
||||||
|
|
||||||
this.power = negPower ? value.substring(1, slashPos) : value.substring(0, slashPos);
|
this.power = k[0];
|
||||||
this.toughness = negToughness ? value.substring(slashPos + 2) : value.substring(slashPos + 1);
|
this.toughness = k[1];
|
||||||
|
|
||||||
this.iPower = StringUtils.isNumeric(this.power) ? Integer.parseInt(this.power) : 0;
|
this.iPower = parsePT(k[0]);
|
||||||
this.iToughness = StringUtils.isNumeric(this.toughness) ? Integer.parseInt(this.toughness) : 0;
|
this.iToughness = parsePT(k[1]);
|
||||||
|
}
|
||||||
|
|
||||||
if (negPower) { this.iPower *= -1; }
|
static int parsePT(String val) {
|
||||||
if (negToughness) { this.iToughness *= -1; }
|
// normalize PT value
|
||||||
|
if (val.contains("*")) {
|
||||||
|
val = val.replace("+*", "");
|
||||||
|
val = val.replace("-*", "");
|
||||||
|
val = val.replace("*", "0");
|
||||||
|
}
|
||||||
|
return Integer.parseInt(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raw fields used for Card creation
|
// Raw fields used for Card creation
|
||||||
|
|||||||
@@ -76,6 +76,51 @@ public final class CardFacePredicates {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ValidPredicate implements Predicate<ICardFace> {
|
||||||
|
private String valid;
|
||||||
|
|
||||||
|
public ValidPredicate(final String valid) {
|
||||||
|
this.valid = valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(ICardFace input) {
|
||||||
|
String k[] = valid.split("\\.", 2);
|
||||||
|
|
||||||
|
if ("Card".equals(k[0])) {
|
||||||
|
// okay
|
||||||
|
} else if ("Permanent".equals(k[0])) {
|
||||||
|
if (input.getType().isInstant() || input.getType().isSorcery()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!input.getType().hasStringType(k[0])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (k.length > 1) {
|
||||||
|
for (final String m : k[1].split("\\+")) {
|
||||||
|
if (!hasProperty(input, m)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static protected boolean hasProperty(ICardFace input, final String v) {
|
||||||
|
if (v.startsWith("non")) {
|
||||||
|
return !hasProperty(input, v.substring(3));
|
||||||
|
} else if (!input.getType().hasStringType(v)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<ICardFace> valid(final String val) {
|
||||||
|
return new ValidPredicate(val);
|
||||||
|
}
|
||||||
|
|
||||||
public static class Presets {
|
public static class Presets {
|
||||||
/** The Constant isBasicLand. */
|
/** The Constant isBasicLand. */
|
||||||
public static final Predicate<ICardFace> IS_BASIC_LAND = new Predicate<ICardFace>() {
|
public static final Predicate<ICardFace> IS_BASIC_LAND = new Predicate<ICardFace>() {
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
private CardAiHints aiHints;
|
private CardAiHints aiHints;
|
||||||
private ColorSet colorIdentity;
|
private ColorSet colorIdentity;
|
||||||
private String meldWith;
|
private String meldWith;
|
||||||
|
private String partnerWith;
|
||||||
|
|
||||||
private CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
|
private CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
|
||||||
splitType = altMode;
|
splitType = altMode;
|
||||||
@@ -48,6 +49,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
otherPart = faces[1];
|
otherPart = faces[1];
|
||||||
aiHints = cah;
|
aiHints = cah;
|
||||||
meldWith = "";
|
meldWith = "";
|
||||||
|
partnerWith = "";
|
||||||
|
|
||||||
//calculate color identity
|
//calculate color identity
|
||||||
byte colMask = calculateColorIdentity(mainPart);
|
byte colMask = calculateColorIdentity(mainPart);
|
||||||
@@ -68,6 +70,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
aiHints = newRules.aiHints;
|
aiHints = newRules.aiHints;
|
||||||
colorIdentity = newRules.colorIdentity;
|
colorIdentity = newRules.colorIdentity;
|
||||||
meldWith = newRules.meldWith;
|
meldWith = newRules.meldWith;
|
||||||
|
partnerWith = newRules.partnerWith;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte calculateColorIdentity(final ICardFace face) {
|
private static byte calculateColorIdentity(final ICardFace face) {
|
||||||
@@ -204,7 +207,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean canBePartnerCommander() {
|
public boolean canBePartnerCommander() {
|
||||||
return canBeCommander() && Iterables.contains(mainPart.getKeywords(), "Partner");
|
return canBeCommander() && (hasKeyword("Partner") || !this.partnerWith.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canBeBrawlCommander() {
|
public boolean canBeBrawlCommander() {
|
||||||
@@ -216,6 +219,10 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
return meldWith;
|
return meldWith;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getParterWith() {
|
||||||
|
return partnerWith;
|
||||||
|
}
|
||||||
|
|
||||||
// vanguard card fields, they don't use sides.
|
// vanguard card fields, they don't use sides.
|
||||||
private int deltaHand;
|
private int deltaHand;
|
||||||
private int deltaLife;
|
private int deltaLife;
|
||||||
@@ -262,6 +269,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
private int curFace = 0;
|
private int curFace = 0;
|
||||||
private CardSplitType altMode = CardSplitType.None;
|
private CardSplitType altMode = CardSplitType.None;
|
||||||
private String meldWith = "";
|
private String meldWith = "";
|
||||||
|
private String partnerWith = "";
|
||||||
private String handLife = null;
|
private String handLife = null;
|
||||||
private String normalizedName = "";
|
private String normalizedName = "";
|
||||||
|
|
||||||
@@ -291,6 +299,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
this.hints = null;
|
this.hints = null;
|
||||||
this.has = null;
|
this.has = null;
|
||||||
this.meldWith = "";
|
this.meldWith = "";
|
||||||
|
this.partnerWith = "";
|
||||||
this.normalizedName = "";
|
this.normalizedName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +316,7 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
result.setNormalizedName(this.normalizedName);
|
result.setNormalizedName(this.normalizedName);
|
||||||
result.meldWith = this.meldWith;
|
result.meldWith = this.meldWith;
|
||||||
|
result.partnerWith = this.partnerWith;
|
||||||
result.setDlUrls(pictureUrl);
|
result.setDlUrls(pictureUrl);
|
||||||
if (StringUtils.isNotBlank(handLife))
|
if (StringUtils.isNotBlank(handLife))
|
||||||
result.setVanguardProperties(handLife);
|
result.setVanguardProperties(handLife);
|
||||||
@@ -382,6 +392,9 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
case 'K':
|
case 'K':
|
||||||
if ("K".equals(key)) {
|
if ("K".equals(key)) {
|
||||||
this.faces[this.curFace].addKeyword(value);
|
this.faces[this.curFace].addKeyword(value);
|
||||||
|
if (value.startsWith("Partner:")) {
|
||||||
|
this.partnerWith = value.split(":")[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -533,4 +546,8 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasKeyword(final String k) {
|
||||||
|
return Iterables.contains(mainPart.getKeywords(), k);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import com.google.common.collect.Iterables;
|
|||||||
|
|
||||||
import forge.util.ComparableOp;
|
import forge.util.ComparableOp;
|
||||||
import forge.util.PredicateString;
|
import forge.util.PredicateString;
|
||||||
import forge.util.PredicateString.StringOp;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filtering conditions specific for CardRules class, defined here along with
|
* Filtering conditions specific for CardRules class, defined here along with
|
||||||
@@ -558,6 +557,19 @@ public final class CardRulesPredicates {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final Predicate<CardRules> CAN_BE_COMMANDER = new Predicate<CardRules>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(final CardRules subject) {
|
||||||
|
return subject.canBeCommander();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Predicate<CardRules> CAN_BE_PARTNER_COMMANDER = new Predicate<CardRules>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(final CardRules subject) {
|
||||||
|
return subject.canBePartnerCommander();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true, CardType.CoreType.Planeswalker);
|
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true, CardType.CoreType.Planeswalker);
|
||||||
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardType.CoreType.Instant);
|
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardType.CoreType.Instant);
|
||||||
public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardType.CoreType.Sorcery);
|
public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardType.CoreType.Sorcery);
|
||||||
@@ -570,13 +582,10 @@ public final class CardRulesPredicates {
|
|||||||
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_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> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(Presets.IS_CREATURE, Presets.IS_LAND));
|
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(Presets.IS_CREATURE, Presets.IS_LAND));
|
||||||
public static final Predicate<CardRules> CAN_BE_COMMANDER = Predicates.or(CardRulesPredicates.rules(StringOp.CONTAINS_IC, "can be your commander"),
|
|
||||||
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
|
|
||||||
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
|
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
|
||||||
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
|
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
|
||||||
|
|
||||||
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
|
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static final Predicate<CardRules> IS_NONCREATURE_SPELL_FOR_GENERATOR = com.google.common.base.Predicates
|
public static final Predicate<CardRules> IS_NONCREATURE_SPELL_FOR_GENERATOR = com.google.common.base.Predicates
|
||||||
.or(Presets.IS_SORCERY, Presets.IS_INSTANT, Presets.IS_PLANESWALKER, Presets.IS_ENCHANTMENT,
|
.or(Presets.IS_SORCERY, Presets.IS_INSTANT, Presets.IS_PLANESWALKER, Presets.IS_ENCHANTMENT,
|
||||||
Predicates.and(Presets.IS_ARTIFACT, Predicates.not(Presets.IS_CREATURE)));
|
Predicates.and(Presets.IS_ARTIFACT, Predicates.not(Presets.IS_CREATURE)));
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
|
|
||||||
public boolean setCreatureTypes(Collection<String> ctypes) {
|
public boolean setCreatureTypes(Collection<String> ctypes) {
|
||||||
// if it isn't a creature then this has no effect
|
// if it isn't a creature then this has no effect
|
||||||
if (!coreTypes.contains(CoreType.Creature)) {
|
if (!isCreature() && !isTribal()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean changed = Iterables.removeIf(subtypes, Predicates.IS_CREATURE_TYPE);
|
boolean changed = Iterables.removeIf(subtypes, Predicates.IS_CREATURE_TYPE);
|
||||||
@@ -236,7 +236,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
final Set<String> landTypes = Sets.newHashSet();
|
final Set<String> landTypes = Sets.newHashSet();
|
||||||
if (isLand()) {
|
if (isLand()) {
|
||||||
for (final String t : subtypes) {
|
for (final String t : subtypes) {
|
||||||
if (isALandType(t) || isABasicLandType(t)) {
|
if (isALandType(t)) {
|
||||||
landTypes.add(t);
|
landTypes.add(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,6 +435,9 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
@Override
|
@Override
|
||||||
public CardTypeView getTypeWithChanges(final Iterable<CardChangedType> changedCardTypes) {
|
public CardTypeView getTypeWithChanges(final Iterable<CardChangedType> changedCardTypes) {
|
||||||
CardType newType = null;
|
CardType newType = null;
|
||||||
|
if (Iterables.isEmpty(changedCardTypes)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
// we assume that changes are already correctly ordered (taken from TreeMap.values())
|
// we assume that changes are already correctly ordered (taken from TreeMap.values())
|
||||||
for (final CardChangedType ct : changedCardTypes) {
|
for (final CardChangedType ct : changedCardTypes) {
|
||||||
if(null == newType)
|
if(null == newType)
|
||||||
@@ -449,7 +452,10 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
if (ct.isRemoveSubTypes()) {
|
if (ct.isRemoveSubTypes()) {
|
||||||
newType.subtypes.clear();
|
newType.subtypes.clear();
|
||||||
}
|
}
|
||||||
else {
|
else if (!newType.subtypes.isEmpty()) {
|
||||||
|
if (ct.isRemoveLandTypes()) {
|
||||||
|
Iterables.removeIf(newType.subtypes, Predicates.IS_LAND_TYPE);
|
||||||
|
}
|
||||||
if (ct.isRemoveCreatureTypes()) {
|
if (ct.isRemoveCreatureTypes()) {
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_CREATURE_TYPE);
|
Iterables.removeIf(newType.subtypes, Predicates.IS_CREATURE_TYPE);
|
||||||
// need to remove AllCreatureTypes too when removing creature Types
|
// need to remove AllCreatureTypes too when removing creature Types
|
||||||
@@ -458,6 +464,9 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
if (ct.isRemoveArtifactTypes()) {
|
if (ct.isRemoveArtifactTypes()) {
|
||||||
Iterables.removeIf(newType.subtypes, Predicates.IS_ARTIFACT_TYPE);
|
Iterables.removeIf(newType.subtypes, Predicates.IS_ARTIFACT_TYPE);
|
||||||
}
|
}
|
||||||
|
if (ct.isRemoveEnchantmentTypes()) {
|
||||||
|
Iterables.removeIf(newType.subtypes, Predicates.IS_ENCHANTMENT_TYPE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ct.getRemoveType() != null) {
|
if (ct.getRemoveType() != null) {
|
||||||
newType.removeAll(ct.getRemoveType());
|
newType.removeAll(ct.getRemoveType());
|
||||||
@@ -466,6 +475,28 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
newType.addAll(ct.getAddType());
|
newType.addAll(ct.getAddType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// sanisfy subtypes
|
||||||
|
if (newType != null && !newType.subtypes.isEmpty()) {
|
||||||
|
if (!newType.isCreature() && !newType.isTribal()) {
|
||||||
|
Iterables.removeIf(newType.subtypes, Predicates.IS_CREATURE_TYPE);
|
||||||
|
newType.subtypes.remove("AllCreatureTypes");
|
||||||
|
}
|
||||||
|
if (!newType.isLand()) {
|
||||||
|
Iterables.removeIf(newType.subtypes, Predicates.IS_LAND_TYPE);
|
||||||
|
}
|
||||||
|
if (!newType.isArtifact()) {
|
||||||
|
Iterables.removeIf(newType.subtypes, Predicates.IS_ARTIFACT_TYPE);
|
||||||
|
}
|
||||||
|
if (!newType.isEnchantment()) {
|
||||||
|
Iterables.removeIf(newType.subtypes, Predicates.IS_ENCHANTMENT_TYPE);
|
||||||
|
}
|
||||||
|
if (!newType.isInstant() && !newType.isSorcery()) {
|
||||||
|
Iterables.removeIf(newType.subtypes, Predicates.IS_SPELL_TYPE);
|
||||||
|
}
|
||||||
|
if (!newType.isPlaneswalker() && !newType.isEmblem()) {
|
||||||
|
Iterables.removeIf(newType.subtypes, Predicates.IS_WALKER_TYPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
return newType == null ? this : newType;
|
return newType == null ? this : newType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,6 +605,13 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
public static final BiMap<String,String> singularTypes = pluralTypes.inverse();
|
public static final BiMap<String,String> singularTypes = pluralTypes.inverse();
|
||||||
}
|
}
|
||||||
public static class Predicates {
|
public static class Predicates {
|
||||||
|
public static Predicate<String> IS_LAND_TYPE = new Predicate<String>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(String input) {
|
||||||
|
return CardType.isALandType(input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static Predicate<String> IS_ARTIFACT_TYPE = new Predicate<String>() {
|
public static Predicate<String> IS_ARTIFACT_TYPE = new Predicate<String>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(String input) {
|
public boolean apply(String input) {
|
||||||
@@ -587,6 +625,27 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
return CardType.isACreatureType(input);
|
return CardType.isACreatureType(input);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static Predicate<String> IS_ENCHANTMENT_TYPE = new Predicate<String>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(String input) {
|
||||||
|
return CardType.isAnEnchantmentType(input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Predicate<String> IS_SPELL_TYPE = new Predicate<String>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(String input) {
|
||||||
|
return CardType.isASpellType(input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Predicate<String> IS_WALKER_TYPE = new Predicate<String>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(String input) {
|
||||||
|
return CardType.isAPlaneswalkerType(input);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -656,7 +715,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isALandType(final String cardType) {
|
public static boolean isALandType(final String cardType) {
|
||||||
return (Constant.LAND_TYPES.contains(cardType));
|
return Constant.LAND_TYPES.contains(cardType) || isABasicLandType(cardType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isAPlaneswalkerType(final String cardType) {
|
public static boolean isAPlaneswalkerType(final String cardType) {
|
||||||
@@ -667,6 +726,13 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
return (Constant.BASIC_TYPES.contains(cardType));
|
return (Constant.BASIC_TYPES.contains(cardType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isAnEnchantmentType(final String cardType) {
|
||||||
|
return (Constant.ENCHANTMENT_TYPES.contains(cardType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isASpellType(final String cardType) {
|
||||||
|
return (Constant.SPELL_TYPES.contains(cardType));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the input is a plural type, return the corresponding singular form.
|
* If the input is a plural type, return the corresponding singular form.
|
||||||
|
|||||||
@@ -291,17 +291,13 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void importDeck(Deck deck) {
|
|
||||||
deck.loadDeferredSections();
|
|
||||||
|
|
||||||
for (DeckSection section: deck.parts.keySet()) {
|
|
||||||
this.putSection(section, deck.get(section));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getImageKey(boolean altState) {
|
public String getImageKey(boolean altState) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Deck getHumanDeck() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,10 +18,8 @@
|
|||||||
package forge.deck;
|
package forge.deck;
|
||||||
|
|
||||||
import forge.item.InventoryItem;
|
import forge.item.InventoryItem;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
public abstract class DeckBase implements Serializable, Comparable<DeckBase>, InventoryItem {
|
public abstract class DeckBase implements Serializable, Comparable<DeckBase>, InventoryItem {
|
||||||
private static final long serialVersionUID = -7538150536939660052L;
|
private static final long serialVersionUID = -7538150536939660052L;
|
||||||
// gameType is from Constant.GameType, like GameType.Regular
|
// gameType is from Constant.GameType, like GameType.Regular
|
||||||
@@ -74,6 +72,7 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase>, In
|
|||||||
public String getDirectory() {
|
public String getDirectory() {
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDirectory(String directory0) {
|
public void setDirectory(String directory0) {
|
||||||
directory = directory0;
|
directory = directory0;
|
||||||
}
|
}
|
||||||
@@ -149,5 +148,5 @@ public abstract class DeckBase implements Serializable, Comparable<DeckBase>, In
|
|||||||
|
|
||||||
public abstract boolean isEmpty();
|
public abstract boolean isEmpty();
|
||||||
|
|
||||||
public abstract void importDeck(Deck deck);
|
public abstract Deck getHumanDeck();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,41 +248,33 @@ public enum DeckFormat {
|
|||||||
return "too many commanders";
|
return "too many commanders";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bring values up to 100
|
|
||||||
min++;
|
|
||||||
max++;
|
|
||||||
|
|
||||||
byte cmdCI = 0;
|
byte cmdCI = 0;
|
||||||
Boolean hasPartner = null;
|
|
||||||
for (PaperCard pc : commanders) {
|
for (PaperCard pc : commanders) {
|
||||||
// For each commander decrement size by 1 (99 for 1, 98 for 2)
|
|
||||||
min--;
|
|
||||||
max--;
|
|
||||||
|
|
||||||
if (!isLegalCommander(pc.getRules())) {
|
if (!isLegalCommander(pc.getRules())) {
|
||||||
return "has an illegal commander";
|
return "has an illegal commander";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPartner != null && !hasPartner) {
|
|
||||||
return "has an illegal commander partnership";
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isPartner = false;
|
|
||||||
for(String s : pc.getRules().getMainPart().getKeywords()) {
|
|
||||||
if (s.equals("Partner")) {
|
|
||||||
isPartner = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasPartner == null) {
|
|
||||||
hasPartner = isPartner;
|
|
||||||
} else if (!isPartner) {
|
|
||||||
return "has an illegal commander partnership";
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdCI |= pc.getRules().getColorIdentity().getColor();
|
cmdCI |= pc.getRules().getColorIdentity().getColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// special check for Partner
|
||||||
|
if (commanders.size() == 2) {
|
||||||
|
// two commander = 98 cards
|
||||||
|
min--;
|
||||||
|
max--;
|
||||||
|
|
||||||
|
PaperCard a = commanders.get(0);
|
||||||
|
PaperCard b = commanders.get(1);
|
||||||
|
|
||||||
|
if (a.getRules().hasKeyword("Partner") && b.getRules().hasKeyword("Partner")) {
|
||||||
|
// normal partner commander
|
||||||
|
} else if (a.getName().equals(b.getRules().getParterWith())
|
||||||
|
&& b.getName().equals(a.getRules().getParterWith())) {
|
||||||
|
// paired partner commander
|
||||||
|
} else {
|
||||||
|
return "has an illegal commander partnership";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final List<PaperCard> erroneousCI = new ArrayList<PaperCard>();
|
final List<PaperCard> erroneousCI = new ArrayList<PaperCard>();
|
||||||
|
|
||||||
Set<String> basicLandNames = new HashSet<>();
|
Set<String> basicLandNames = new HashSet<>();
|
||||||
@@ -521,6 +513,8 @@ public enum DeckFormat {
|
|||||||
for (final PaperCard p : commanders) {
|
for (final PaperCard p : commanders) {
|
||||||
cmdCI |= p.getRules().getColorIdentity().getColor();
|
cmdCI |= p.getRules().getColorIdentity().getColor();
|
||||||
}
|
}
|
||||||
return Predicates.compose(Predicates.or(CardRulesPredicates.hasColorIdentity(cmdCI), CardRulesPredicates.hasKeyword("Partner")), PaperCard.FN_GET_RULES);
|
// TODO : check commander what kind of Partner it needs
|
||||||
|
return Predicates.compose(Predicates.or(CardRulesPredicates.hasColorIdentity(cmdCI),
|
||||||
|
CardRulesPredicates.Presets.CAN_BE_PARTNER_COMMANDER), PaperCard.FN_GET_RULES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,6 @@
|
|||||||
package forge.deck;
|
package forge.deck;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import forge.StaticData;
|
|
||||||
import forge.item.PaperCard;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +44,8 @@ public class DeckGroup extends DeckBase {
|
|||||||
*
|
*
|
||||||
* @return the human deck
|
* @return the human deck
|
||||||
*/
|
*/
|
||||||
public final Deck getHumanDeck() {
|
@Override
|
||||||
|
public Deck getHumanDeck() {
|
||||||
return humanDeck;
|
return humanDeck;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,100 +158,4 @@ public class DeckGroup extends DeckBase {
|
|||||||
public String getImageKey(boolean altState) {
|
public String getImageKey(boolean altState) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void importDeck(Deck deck) {
|
|
||||||
CardPool draftedCards = this.getHumanDeck().getAllCardsInASinglePool(false);
|
|
||||||
|
|
||||||
this.getHumanDeck().putSection(DeckSection.Main, new CardPool());
|
|
||||||
this.getHumanDeck().putSection(DeckSection.Sideboard, new CardPool());
|
|
||||||
|
|
||||||
HashMap<String, Integer> countByName = getCountByName(deck);
|
|
||||||
|
|
||||||
addFromDraftedCardPool(countByName, draftedCards);
|
|
||||||
addBasicLands(deck, countByName, draftedCards);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HashMap<String, Integer> getCountByName(Deck deck) {
|
|
||||||
HashMap<String, Integer> result = new HashMap<String, Integer>();
|
|
||||||
|
|
||||||
for (Map.Entry<PaperCard, Integer> entry: deck.getMain()) {
|
|
||||||
PaperCard importedCard = entry.getKey();
|
|
||||||
|
|
||||||
Integer previousCount = result.getOrDefault(importedCard.getName(), 0);
|
|
||||||
int countToAdd = entry.getValue();
|
|
||||||
|
|
||||||
result.put(importedCard.getName(), countToAdd + previousCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addFromDraftedCardPool(HashMap<String, Integer> countByName, CardPool availableCards) {
|
|
||||||
for (Map.Entry<PaperCard, Integer> entry: availableCards) {
|
|
||||||
|
|
||||||
PaperCard availableCard = entry.getKey();
|
|
||||||
Integer availableCount = entry.getValue();
|
|
||||||
int countToAdd = countByName.getOrDefault(availableCard.getName(), 0);
|
|
||||||
|
|
||||||
if (availableCard.getRules().getType().isBasicLand()) {
|
|
||||||
// basic lands are added regardless from drafted cards
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int countMain = Math.min(availableCount, countToAdd);
|
|
||||||
|
|
||||||
if (countMain > 0) {
|
|
||||||
this.getHumanDeck().getMain().add(availableCard, countMain);
|
|
||||||
countByName.put(availableCard.getName(), countToAdd - countMain);
|
|
||||||
}
|
|
||||||
|
|
||||||
int countSideboard = availableCount - countMain;
|
|
||||||
|
|
||||||
if (countSideboard > 0) {
|
|
||||||
CardPool sideboard = this.getHumanDeck().getOrCreate(DeckSection.Sideboard);
|
|
||||||
sideboard.add(availableCard, countSideboard);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addBasicLands(Deck deck, HashMap<String, Integer> countByName, CardPool availableCards) {
|
|
||||||
HashMap<String, PaperCard> basicLandsByName = getBasicLandsByName(deck, countByName);
|
|
||||||
|
|
||||||
Date dateWithAllCards = StaticData.instance().getEditions().getEarliestDateWithAllCards(availableCards);
|
|
||||||
for (String cardName: countByName.keySet()) {
|
|
||||||
|
|
||||||
PaperCard card = basicLandsByName.getOrDefault(cardName, null);
|
|
||||||
|
|
||||||
if (card == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int countToAdd = countByName.get(cardName);
|
|
||||||
|
|
||||||
card = StaticData.instance().getCardByEditionDate(card, dateWithAllCards);
|
|
||||||
this.getHumanDeck().getMain().add(card.getName(), card.getEdition(), countToAdd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private HashMap<String, PaperCard> getBasicLandsByName(Deck deck, HashMap<String, Integer> countByName) {
|
|
||||||
HashMap<String, PaperCard> result = new HashMap<String, PaperCard>();
|
|
||||||
|
|
||||||
for (Map.Entry<PaperCard, Integer> entry: deck.getMain()) {
|
|
||||||
PaperCard card = entry.getKey();
|
|
||||||
|
|
||||||
if (!card.getRules().getType().isBasicLand()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.containsKey(card.getName())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.put(card.getName(), card);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ public enum DeckSection {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String valToCompate = value.trim();
|
final String valToCompare = value.trim();
|
||||||
for (final DeckSection v : DeckSection.values()) {
|
for (final DeckSection v : DeckSection.values()) {
|
||||||
if (v.name().compareToIgnoreCase(valToCompate) == 0) {
|
if (v.name().compareToIgnoreCase(valToCompare) == 0) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,6 +137,6 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getImageKey(boolean altState) {
|
public String getImageKey(boolean altState) {
|
||||||
return ImageKeys.TOKEN_PREFIX + imageFileName;
|
return ImageKeys.TOKEN_PREFIX + imageFileName.replace(" ", "_");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,15 +39,21 @@ public class TokenDb implements ITokenDatabase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PaperToken getToken(String tokenName, String edition) {
|
public PaperToken getToken(String tokenName, String edition) {
|
||||||
|
String fullName = String.format("%s_%s", tokenName, edition.toLowerCase());
|
||||||
|
|
||||||
|
if (!tokensByName.containsKey(fullName)) {
|
||||||
try {
|
try {
|
||||||
PaperToken pt = new PaperToken(rulesByName.get(tokenName), editions.get(edition));
|
PaperToken pt = new PaperToken(rulesByName.get(tokenName), editions.get(edition));
|
||||||
// TODO Cache the token after it's referenced
|
tokensByName.put(fullName, pt);
|
||||||
return pt;
|
return pt;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return tokensByName.get(fullName);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PaperToken getToken(String tokenName, String edition, int artIndex) {
|
public PaperToken getToken(String tokenName, String edition, int artIndex) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.11-SNAPSHOT</version>
|
<version>1.6.16-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-game</artifactId>
|
<artifactId>forge-game</artifactId>
|
||||||
@@ -30,5 +30,37 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
<type>jar</type>
|
<type>jar</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.sentry</groupId>
|
||||||
|
<artifactId>sentry-log4j</artifactId>
|
||||||
|
<version>1.7.5</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>checkstyle-validation</id>
|
||||||
|
<phase>validate</phase>
|
||||||
|
<configuration>
|
||||||
|
<configLocation>../checkstyle.xml</configLocation>
|
||||||
|
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<consoleOutput>true</consoleOutput>
|
||||||
|
<failsOnError>true</failsOnError>
|
||||||
|
<failOnViolation>true</failOnViolation>
|
||||||
|
</configuration>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -314,7 +314,11 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
|
|||||||
list.addAll(p.getCardsIn(presentZone));
|
list.addAll(p.getCardsIn(presentZone));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (presentPlayer.equals("Any")) {
|
||||||
|
for (final Player p : this.getHostCard().getController().getAllies()) {
|
||||||
|
list.addAll(p.getCardsIn(presentZone));
|
||||||
|
}
|
||||||
|
}
|
||||||
list = CardLists.getValidCards(list, sIsPresent.split(","), this.getHostCard().getController(), this.getHostCard(), null);
|
list = CardLists.getValidCards(list, sIsPresent.split(","), this.getHostCard().getController(), this.getHostCard(), null);
|
||||||
|
|
||||||
int right = 1;
|
int right = 1;
|
||||||
|
|||||||
@@ -168,6 +168,10 @@ public class ForgeScript {
|
|||||||
if (!sa.isFlashBackAbility()) {
|
if (!sa.isFlashBackAbility()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if (property.equals("Jumpstart")) {
|
||||||
|
if (!sa.isJumpstart()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if (property.equals("Kicked")) {
|
} else if (property.equals("Kicked")) {
|
||||||
if (!sa.isKicked()) {
|
if (!sa.isKicked()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -204,6 +208,12 @@ public class ForgeScript {
|
|||||||
if (!found) {
|
if (!found) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if (property.equals("YouCtrl")) {
|
||||||
|
return sa.getActivatingPlayer().equals(sourceController);
|
||||||
|
} else if (sa.getHostCard() != null) {
|
||||||
|
if (!sa.getHostCard().hasProperty(property, sourceController, source, spellAbility)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package forge.game;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.*;
|
import com.google.common.collect.*;
|
||||||
import forge.GameCommand;
|
import forge.GameCommand;
|
||||||
|
import forge.StaticData;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
@@ -150,8 +151,9 @@ public class GameAction {
|
|||||||
// Cards returned from exile face-down must be reset to their original state, otherwise
|
// Cards returned from exile face-down must be reset to their original state, otherwise
|
||||||
// all sort of funky shenanigans may happen later (e.g. their ETB replacement effects are set
|
// all sort of funky shenanigans may happen later (e.g. their ETB replacement effects are set
|
||||||
// up on the wrong card state etc.).
|
// up on the wrong card state etc.).
|
||||||
if (zoneTo.is(ZoneType.Hand) && zoneFrom.is(ZoneType.Exile) && c.isFaceDown()) {
|
if (c.isFaceDown() && (fromBattlefield || (toHand && zoneFrom.is(ZoneType.Exile)))) {
|
||||||
c.setState(CardStateName.Original, true);
|
c.setState(CardStateName.Original, true);
|
||||||
|
c.runFaceupCommands();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the temporary Dash SVar when the Dashed card leaves the battlefield
|
// Clean up the temporary Dash SVar when the Dashed card leaves the battlefield
|
||||||
@@ -190,7 +192,7 @@ public class GameAction {
|
|||||||
|
|
||||||
if (!c.isToken()) {
|
if (!c.isToken()) {
|
||||||
if (c.isCloned()) {
|
if (c.isCloned()) {
|
||||||
c.switchStates(CardStateName.Cloner, CardStateName.Original, false);
|
c.switchStates(CardStateName.Original, CardStateName.Cloner, false);
|
||||||
c.setState(CardStateName.Original, false);
|
c.setState(CardStateName.Original, false);
|
||||||
c.clearStates(CardStateName.Cloner, false);
|
c.clearStates(CardStateName.Cloner, false);
|
||||||
if (c.isFlipCard()) {
|
if (c.isFlipCard()) {
|
||||||
@@ -254,7 +256,20 @@ public class GameAction {
|
|||||||
CardCollection preList = new CardCollection(noLandLKI);
|
CardCollection preList = new CardCollection(noLandLKI);
|
||||||
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
|
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
|
||||||
|
|
||||||
|
// fake etb counters thing, then if something changed,
|
||||||
|
// need to apply checkStaticAbilities again
|
||||||
|
if(!noLandLKI.isLand()) {
|
||||||
|
if (noLandLKI.putEtbCounters()) {
|
||||||
|
// counters are added need to check again
|
||||||
|
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(noLandLKI.isLand()) {
|
if(noLandLKI.isLand()) {
|
||||||
|
// if it isn't on the Stack, it stays in that Zone
|
||||||
|
if (!c.getZone().is(ZoneType.Stack)) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
// if something would only be a land when entering the battlefield and not before
|
// if something would only be a land when entering the battlefield and not before
|
||||||
// put it into the graveyard instead
|
// put it into the graveyard instead
|
||||||
zoneTo = c.getOwner().getZone(ZoneType.Graveyard);
|
zoneTo = c.getOwner().getZone(ZoneType.Graveyard);
|
||||||
@@ -262,6 +277,9 @@ public class GameAction {
|
|||||||
copied.setState(CardStateName.Original, false);
|
copied.setState(CardStateName.Original, false);
|
||||||
copied.setManifested(false);
|
copied.setManifested(false);
|
||||||
copied.updateStateForView();
|
copied.updateStateForView();
|
||||||
|
|
||||||
|
// not to battlefield anymore!
|
||||||
|
toBattlefield = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,7 +375,10 @@ public class GameAction {
|
|||||||
// do ETB counters after StaticAbilities check
|
// do ETB counters after StaticAbilities check
|
||||||
if (!suppress) {
|
if (!suppress) {
|
||||||
if (toBattlefield) {
|
if (toBattlefield) {
|
||||||
copied.putEtbCounters();
|
if (copied.putEtbCounters()) {
|
||||||
|
// if counter where put of card, call checkStaticAbilities again
|
||||||
|
checkStaticAbilities();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
copied.clearEtbCounters();
|
copied.clearEtbCounters();
|
||||||
}
|
}
|
||||||
@@ -367,6 +388,7 @@ public class GameAction {
|
|||||||
|
|
||||||
final Map<String, Object> runParams = Maps.newHashMap();
|
final Map<String, Object> runParams = Maps.newHashMap();
|
||||||
runParams.put("Card", lastKnownInfo);
|
runParams.put("Card", lastKnownInfo);
|
||||||
|
runParams.put("Cause", cause);
|
||||||
runParams.put("Origin", zoneFrom != null ? zoneFrom.getZoneType().name() : null);
|
runParams.put("Origin", zoneFrom != null ? zoneFrom.getZoneType().name() : null);
|
||||||
runParams.put("Destination", zoneTo.getZoneType().name());
|
runParams.put("Destination", zoneTo.getZoneType().name());
|
||||||
runParams.put("SpellAbilityStackInstance", game.stack.peek());
|
runParams.put("SpellAbilityStackInstance", game.stack.peek());
|
||||||
@@ -873,9 +895,13 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runEvents) {
|
// preList means that this is run by a pre Check with LKI objects
|
||||||
|
// in that case Always trigger should not Run
|
||||||
|
if (preList.isEmpty()) {
|
||||||
final Map<String, Object> runParams = Maps.newHashMap();
|
final Map<String, Object> runParams = Maps.newHashMap();
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false);
|
||||||
|
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.Immediate, runParams, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update P/T and type in the view only once after all the cards have been processed, to avoid flickering
|
// Update P/T and type in the view only once after all the cards have been processed, to avoid flickering
|
||||||
@@ -1562,6 +1588,53 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void drawStartingHand(Player p1){
|
||||||
|
|
||||||
|
//check initial hand
|
||||||
|
List<Card> lib1 = Lists.newArrayList(p1.getZone(ZoneType.Library).getCards().threadSafeIterable());
|
||||||
|
List<Card> hand1 = lib1.subList(0,p1.getMaxHandSize());
|
||||||
|
System.out.println(hand1.toString());
|
||||||
|
|
||||||
|
//shuffle
|
||||||
|
List<Card> shuffledCards = Lists.newArrayList(p1.getZone(ZoneType.Library).getCards().threadSafeIterable());
|
||||||
|
Collections.shuffle(shuffledCards);
|
||||||
|
|
||||||
|
//check a second hand
|
||||||
|
List<Card> hand2 = shuffledCards.subList(0,p1.getMaxHandSize());
|
||||||
|
System.out.println(hand2.toString());
|
||||||
|
|
||||||
|
//choose better hand according to land count
|
||||||
|
float averageLandRatio = getLandRatio(lib1);
|
||||||
|
if(getHandScore(hand1, averageLandRatio)>getHandScore(hand2, averageLandRatio)){
|
||||||
|
p1.getZone(ZoneType.Library).setCards(shuffledCards);
|
||||||
|
}
|
||||||
|
p1.drawCards(p1.getMaxHandSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getLandRatio(List<Card> deck){
|
||||||
|
int landCount = 0;
|
||||||
|
for(Card c:deck){
|
||||||
|
if(c.isLand()){
|
||||||
|
landCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (landCount == 0 ){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Float.valueOf(landCount)/Float.valueOf(deck.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getHandScore(List<Card> hand, float landRatio){
|
||||||
|
int landCount = 0;
|
||||||
|
for(Card c:hand){
|
||||||
|
if(c.isLand()){
|
||||||
|
landCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float averageCount = landRatio * hand.size();
|
||||||
|
return Math.abs(averageCount-landCount);
|
||||||
|
}
|
||||||
|
|
||||||
public void startGame(GameOutcome lastGameOutcome) {
|
public void startGame(GameOutcome lastGameOutcome) {
|
||||||
startGame(lastGameOutcome, null);
|
startGame(lastGameOutcome, null);
|
||||||
}
|
}
|
||||||
@@ -1582,7 +1655,11 @@ public class GameAction {
|
|||||||
|
|
||||||
game.setAge(GameStage.Mulligan);
|
game.setAge(GameStage.Mulligan);
|
||||||
for (final Player p1 : game.getPlayers()) {
|
for (final Player p1 : game.getPlayers()) {
|
||||||
p1.drawCards(p1.getMaxHandSize());
|
if (StaticData.instance().getFilteredHandsEnabled() ) {
|
||||||
|
drawStartingHand(p1);
|
||||||
|
} else {
|
||||||
|
p1.drawCards(p1.getStartingHandSize());
|
||||||
|
}
|
||||||
|
|
||||||
// If pl has Backup Plan as a Conspiracy draw that many extra hands
|
// If pl has Backup Plan as a Conspiracy draw that many extra hands
|
||||||
|
|
||||||
@@ -1702,6 +1779,11 @@ public class GameAction {
|
|||||||
boolean isMultiPlayer = game.getPlayers().size() > 2;
|
boolean isMultiPlayer = game.getPlayers().size() > 2;
|
||||||
int mulliganDelta = isMultiPlayer ? 0 : 1;
|
int mulliganDelta = isMultiPlayer ? 0 : 1;
|
||||||
|
|
||||||
|
// https://magic.wizards.com/en/articles/archive/feature/checking-brawl-2018-07-09
|
||||||
|
if (game.getRules().hasAppliedVariant(GameType.Brawl) && !isMultiPlayer){
|
||||||
|
mulliganDelta = 0;
|
||||||
|
}
|
||||||
|
|
||||||
boolean allKept;
|
boolean allKept;
|
||||||
do {
|
do {
|
||||||
allKept = true;
|
allKept = true;
|
||||||
@@ -1766,7 +1848,7 @@ public class GameAction {
|
|||||||
//Vancouver Mulligan
|
//Vancouver Mulligan
|
||||||
for(Player p : whoCanMulligan) {
|
for(Player p : whoCanMulligan) {
|
||||||
if (p.getStartingHandSize() > p.getZone(ZoneType.Hand).size()) {
|
if (p.getStartingHandSize() > p.getZone(ZoneType.Hand).size()) {
|
||||||
p.scry(1);
|
p.scry(1, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,7 +174,8 @@ public final class GameActionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) {
|
if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) {
|
||||||
final SpellAbility newSA = sa.copyWithNoManaCost();
|
// set the cost to this directly to buypass non mana cost
|
||||||
|
final SpellAbility newSA = sa.copyWithDefinedCost("Discard<1/CARDNAME>");
|
||||||
newSA.setBasicSpell(false);
|
newSA.setBasicSpell(false);
|
||||||
newSA.getMapParams().put("CostDesc", ManaCostParser.parse("0"));
|
newSA.getMapParams().put("CostDesc", ManaCostParser.parse("0"));
|
||||||
// makes new SpellDescription
|
// makes new SpellDescription
|
||||||
@@ -186,6 +187,15 @@ public final class GameActionUtil {
|
|||||||
alternatives.add(newSA);
|
alternatives.add(newSA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sa.hasParam("Equip") && activator.hasKeyword("EquipInstantSpeed")) {
|
||||||
|
final SpellAbility newSA = sa.copy(activator);
|
||||||
|
SpellAbilityRestriction sar = newSA.getRestrictions();
|
||||||
|
sar.setSorcerySpeed(false);
|
||||||
|
sar.setInstantSpeed(true);
|
||||||
|
newSA.setDescription(sa.getDescription() + " (you may activate any time you could cast an instant )");
|
||||||
|
alternatives.add(newSA);
|
||||||
|
}
|
||||||
|
|
||||||
for (final KeywordInterface inst : source.getKeywords()) {
|
for (final KeywordInterface inst : source.getKeywords()) {
|
||||||
final String keyword = inst.getOriginal();
|
final String keyword = inst.getOriginal();
|
||||||
if (sa.isSpell() && keyword.startsWith("Flashback")) {
|
if (sa.isSpell() && keyword.startsWith("Flashback")) {
|
||||||
@@ -201,20 +211,12 @@ public final class GameActionUtil {
|
|||||||
flashback.getRestrictions().setZone(ZoneType.Graveyard);
|
flashback.getRestrictions().setZone(ZoneType.Graveyard);
|
||||||
|
|
||||||
// there is a flashback cost (and not the cards cost)
|
// there is a flashback cost (and not the cards cost)
|
||||||
if (!keyword.equals("Flashback")) {
|
if (keyword.contains(":")) {
|
||||||
flashback.setPayCosts(new Cost(keyword.substring(10), false));
|
final String k[] = keyword.split(":");
|
||||||
|
flashback.setPayCosts(new Cost(k[1], false));
|
||||||
}
|
}
|
||||||
alternatives.add(flashback);
|
alternatives.add(flashback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("Equip") && sa instanceof AbilityActivated && keyword.equals("EquipInstantSpeed")) {
|
|
||||||
final SpellAbility newSA = sa.copy(activator);
|
|
||||||
SpellAbilityRestriction sar = newSA.getRestrictions();
|
|
||||||
sar.setSorcerySpeed(false);
|
|
||||||
sar.setInstantSpeed(true);
|
|
||||||
newSA.setDescription(sa.getDescription() + " (you may activate any time you could cast an instant )");
|
|
||||||
alternatives.add(newSA);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return alternatives;
|
return alternatives;
|
||||||
}
|
}
|
||||||
@@ -259,6 +261,11 @@ public final class GameActionUtil {
|
|||||||
final Cost cost = new Cost("Discard<1/Land>", false);
|
final Cost cost = new Cost("Discard<1/Land>", false);
|
||||||
costs.add(new OptionalCostValue(OptionalCost.Retrace, cost));
|
costs.add(new OptionalCostValue(OptionalCost.Retrace, cost));
|
||||||
}
|
}
|
||||||
|
} else if (keyword.equals("Jump-start")) {
|
||||||
|
if (source.getZone().is(ZoneType.Graveyard)) {
|
||||||
|
final Cost cost = new Cost("Discard<1/Card>", false);
|
||||||
|
costs.add(new OptionalCostValue(OptionalCost.Jumpstart, cost));
|
||||||
|
}
|
||||||
} else if (keyword.startsWith("MayFlashCost")) {
|
} else if (keyword.startsWith("MayFlashCost")) {
|
||||||
String[] k = keyword.split(":");
|
String[] k = keyword.split(":");
|
||||||
final Cost cost = new Cost(k[1], false);
|
final Cost cost = new Cost(k[1], false);
|
||||||
@@ -286,6 +293,7 @@ public final class GameActionUtil {
|
|||||||
result.addConspireInstance();
|
result.addConspireInstance();
|
||||||
break;
|
break;
|
||||||
case Retrace:
|
case Retrace:
|
||||||
|
case Jumpstart:
|
||||||
result.getRestrictions().setZone(ZoneType.Graveyard);
|
result.getRestrictions().setZone(ZoneType.Graveyard);
|
||||||
break;
|
break;
|
||||||
case Flash:
|
case Flash:
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ import forge.game.card.CounterType;
|
|||||||
import forge.game.event.GameEventCardAttachment;
|
import forge.game.event.GameEventCardAttachment;
|
||||||
import forge.game.event.GameEventCardAttachment.AttachMethod;
|
import forge.game.event.GameEventCardAttachment.AttachMethod;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.util.collect.FCollection;
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
@@ -337,6 +339,17 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canBeEnchantedBy(final Card aura) {
|
||||||
|
SpellAbility sa = aura.getFirstAttachSpell();
|
||||||
|
TargetRestrictions tgt = null;
|
||||||
|
if (sa != null) {
|
||||||
|
tgt = sa.getTargetRestrictions();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !(hasProtectionFrom(aura)
|
||||||
|
|| ((tgt != null) && !isValid(tgt.getValidTgts(), aura.getController(), aura, sa)));
|
||||||
|
}
|
||||||
|
|
||||||
public abstract boolean hasProtectionFrom(final Card source);
|
public abstract boolean hasProtectionFrom(final Card source);
|
||||||
|
|
||||||
// Counters!
|
// Counters!
|
||||||
@@ -365,7 +378,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 void addCounter(final CounterType counterType, final int n, final Card source, final boolean applyMultiplier, final boolean fireEvents);
|
abstract public int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents);
|
||||||
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();
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import forge.game.event.GameEventPlayerPoisoned;
|
|||||||
import forge.game.event.GameEventScry;
|
import forge.game.event.GameEventScry;
|
||||||
import forge.game.event.GameEventSpellAbilityCast;
|
import forge.game.event.GameEventSpellAbilityCast;
|
||||||
import forge.game.event.GameEventSpellResolved;
|
import forge.game.event.GameEventSpellResolved;
|
||||||
|
import forge.game.event.GameEventSurveil;
|
||||||
import forge.game.event.GameEventTurnBegan;
|
import forge.game.event.GameEventTurnBegan;
|
||||||
import forge.game.event.GameEventTurnPhase;
|
import forge.game.event.GameEventTurnPhase;
|
||||||
import forge.game.event.IGameEventVisitor;
|
import forge.game.event.IGameEventVisitor;
|
||||||
@@ -66,6 +67,23 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
|||||||
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, scryOutcome);
|
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, scryOutcome);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameLogEntry visit(GameEventSurveil ev) {
|
||||||
|
String surveilOutcome = "";
|
||||||
|
String toLibrary = Lang.nounWithAmount(ev.toLibrary, "card") + " to the top of the library";
|
||||||
|
String toGraveyard = Lang.nounWithAmount(ev.toGraveyard, "card") + " to the graveyard";
|
||||||
|
|
||||||
|
if (ev.toLibrary > 0 && ev.toGraveyard > 0) {
|
||||||
|
surveilOutcome = ev.player.toString() + " surveiled " + toLibrary + " and " + toGraveyard;
|
||||||
|
} else if (ev.toGraveyard == 0) {
|
||||||
|
surveilOutcome = ev.player.toString() + " surveiled " + toLibrary;
|
||||||
|
} else {
|
||||||
|
surveilOutcome = ev.player.toString() + " surveiled " + toGraveyard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, surveilOutcome);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GameLogEntry visit(GameEventSpellResolved ev) {
|
public GameLogEntry visit(GameEventSpellResolved ev) {
|
||||||
String messageForLog = ev.hasFizzled ? ev.spell.getHostCard().getName() + " ability fizzles." : ev.spell.getStackDescription();
|
String messageForLog = ev.hasFizzled ? ev.spell.getHostCard().getName() + " ability fizzles." : ev.spell.getStackDescription();
|
||||||
|
|||||||
@@ -58,9 +58,6 @@ public class StaticEffect {
|
|||||||
private String chosenType;
|
private String chosenType;
|
||||||
private Map<String, String> mapParams = Maps.newTreeMap();
|
private Map<String, String> mapParams = Maps.newTreeMap();
|
||||||
|
|
||||||
// for P/T
|
|
||||||
private final Map<Card, String> originalPT = Maps.newTreeMap();
|
|
||||||
|
|
||||||
// for types
|
// for types
|
||||||
private boolean overwriteTypes = false;
|
private boolean overwriteTypes = false;
|
||||||
private boolean keepSupertype = false;
|
private boolean keepSupertype = false;
|
||||||
@@ -101,7 +98,6 @@ public class StaticEffect {
|
|||||||
copy.xValueMap = this.xValueMap;
|
copy.xValueMap = this.xValueMap;
|
||||||
copy.chosenType = this.chosenType;
|
copy.chosenType = this.chosenType;
|
||||||
copy.mapParams = this.mapParams;
|
copy.mapParams = this.mapParams;
|
||||||
map.fillKeyedMap(copy.originalPT, this.originalPT);
|
|
||||||
copy.overwriteTypes = this.overwriteTypes;
|
copy.overwriteTypes = this.overwriteTypes;
|
||||||
copy.keepSupertype = this.keepSupertype;
|
copy.keepSupertype = this.keepSupertype;
|
||||||
copy.removeSubTypes = this.removeSubTypes;
|
copy.removeSubTypes = this.removeSubTypes;
|
||||||
@@ -345,68 +341,6 @@ public class StaticEffect {
|
|||||||
this.originalKeywords.clear();
|
this.originalKeywords.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// original power/toughness
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* addOriginalPT.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param c
|
|
||||||
* a {@link forge.game.card.Card} object.
|
|
||||||
* @param power
|
|
||||||
* a int.
|
|
||||||
* @param toughness
|
|
||||||
* a int.
|
|
||||||
*/
|
|
||||||
public final void addOriginalPT(final Card c, final int power, final int toughness) {
|
|
||||||
final String pt = power + "/" + toughness;
|
|
||||||
if (!this.originalPT.containsKey(c)) {
|
|
||||||
this.originalPT.put(c, pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* getOriginalPower.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param c
|
|
||||||
* a {@link forge.game.card.Card} object.
|
|
||||||
* @return a int.
|
|
||||||
*/
|
|
||||||
public final int getOriginalPower(final Card c) {
|
|
||||||
int power = -1;
|
|
||||||
if (this.originalPT.containsKey(c)) {
|
|
||||||
power = Integer.parseInt(this.originalPT.get(c).split("/")[0]);
|
|
||||||
}
|
|
||||||
return power;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* getOriginalToughness.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param c
|
|
||||||
* a {@link forge.game.card.Card} object.
|
|
||||||
* @return a int.
|
|
||||||
*/
|
|
||||||
public final int getOriginalToughness(final Card c) {
|
|
||||||
int tough = -1;
|
|
||||||
if (this.originalPT.containsKey(c)) {
|
|
||||||
tough = Integer.parseInt(this.originalPT.get(c).split("/")[1]);
|
|
||||||
}
|
|
||||||
return tough;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* clearAllOriginalPTs.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public final void clearAllOriginalPTs() {
|
|
||||||
this.originalPT.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// should we overwrite types?
|
// should we overwrite types?
|
||||||
/**
|
/**
|
||||||
@@ -995,7 +929,7 @@ public class StaticEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove set P/T
|
// remove set P/T
|
||||||
if (!params.containsKey("CharacteristicDefining") && setPT) {
|
if (setPT) {
|
||||||
affectedCard.removeNewPT(getTimestamp());
|
affectedCard.removeNewPT(getTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,7 +970,7 @@ public class StaticEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove abilities
|
// remove abilities
|
||||||
if (params.containsKey("RemoveAllAbilities")) {
|
if (params.containsKey("RemoveAllAbilities") || params.containsKey("RemoveIntrinsicAbilities")) {
|
||||||
affectedCard.unSuppressCardTraits();
|
affectedCard.unSuppressCardTraits();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ public final class AbilityFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (api == ApiType.Charm || api == ApiType.GenericChoice) {
|
if (api == ApiType.Charm || api == ApiType.GenericChoice || api == ApiType.AssignGroup) {
|
||||||
final String key = "Choices";
|
final String key = "Choices";
|
||||||
if (mapParams.containsKey(key)) {
|
if (mapParams.containsKey(key)) {
|
||||||
List<String> names = Lists.newArrayList(mapParams.get(key).split(","));
|
List<String> names = Lists.newArrayList(mapParams.get(key).split(","));
|
||||||
|
|||||||
@@ -381,7 +381,7 @@ public class AbilityUtils {
|
|||||||
svarval = ability.getSVar(amount);
|
svarval = ability.getSVar(amount);
|
||||||
}
|
}
|
||||||
if (StringUtils.isBlank(svarval)) {
|
if (StringUtils.isBlank(svarval)) {
|
||||||
if ((ability != null) && (ability instanceof SpellAbility) && !(ability instanceof SpellPermanent)) {
|
if ((ability != null) && (ability instanceof SpellAbility) && !(ability instanceof SpellPermanent) && !amount.equals("ChosenX")) {
|
||||||
System.err.printf("SVar '%s' not found in ability, fallback to Card (%s). Ability is (%s)%n", amount, card.getName(), ability);
|
System.err.printf("SVar '%s' not found in ability, fallback to Card (%s). Ability is (%s)%n", amount, card.getName(), ability);
|
||||||
}
|
}
|
||||||
svarval = card.getSVar(amount);
|
svarval = card.getSVar(amount);
|
||||||
@@ -439,6 +439,10 @@ public class AbilityUtils {
|
|||||||
players.addAll(game.getPlayers());
|
players.addAll(game.getPlayers());
|
||||||
val = CardFactoryUtil.playerXCount(players, calcX[1], card);
|
val = CardFactoryUtil.playerXCount(players, calcX[1], card);
|
||||||
}
|
}
|
||||||
|
else if (hType.equals("YourTeam")) {
|
||||||
|
players.addAll(player.getYourTeam());
|
||||||
|
val = CardFactoryUtil.playerXCount(players, calcX[1], card);
|
||||||
|
}
|
||||||
else if (hType.equals("Opponents")) {
|
else if (hType.equals("Opponents")) {
|
||||||
players.addAll(player.getOpponents());
|
players.addAll(player.getOpponents());
|
||||||
val = CardFactoryUtil.playerXCount(players, calcX[1], card);
|
val = CardFactoryUtil.playerXCount(players, calcX[1], card);
|
||||||
@@ -891,7 +895,11 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
final Player player = sa == null ? card.getController() : sa.getActivatingPlayer();
|
final Player player = sa == null ? card.getController() : sa.getActivatingPlayer();
|
||||||
|
|
||||||
if (defined.equals("Targeted") || defined.equals("TargetedPlayer")) {
|
if (defined.equals("TargetedOrController")) {
|
||||||
|
players.addAll(getDefinedPlayers(card, "Targeted", sa));
|
||||||
|
players.addAll(getDefinedPlayers(card, "TargetedController", sa));
|
||||||
|
}
|
||||||
|
else if (defined.equals("Targeted") || defined.equals("TargetedPlayer")) {
|
||||||
final SpellAbility saTargeting = sa.getSATargetingPlayer();
|
final SpellAbility saTargeting = sa.getSATargetingPlayer();
|
||||||
if (saTargeting != null) {
|
if (saTargeting != null) {
|
||||||
players.addAll(saTargeting.getTargets().getTargetPlayers());
|
players.addAll(saTargeting.getTargets().getTargetPlayers());
|
||||||
@@ -1672,7 +1680,10 @@ public class AbilityUtils {
|
|||||||
res.setZone(null);
|
res.setZone(null);
|
||||||
newSA.setRestrictions(res);
|
newSA.setRestrictions(res);
|
||||||
// timing restrictions still apply
|
// timing restrictions still apply
|
||||||
if (res.checkTimingRestrictions(tgtCard, newSA) && newSA.checkOtherRestrictions()) {
|
if (res.checkTimingRestrictions(tgtCard, newSA)
|
||||||
|
// still need to check the other restrictions like Aftermath
|
||||||
|
&& res.checkOtherRestrictions(tgtCard, newSA, controller)
|
||||||
|
&& newSA.checkOtherRestrictions()) {
|
||||||
sas.add(newSA);
|
sas.add(newSA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public enum ApiType {
|
|||||||
AnimateAll (AnimateAllEffect.class),
|
AnimateAll (AnimateAllEffect.class),
|
||||||
Attach (AttachEffect.class),
|
Attach (AttachEffect.class),
|
||||||
Ascend (AscendEffect.class),
|
Ascend (AscendEffect.class),
|
||||||
|
AssignGroup (AssignGroupEffect.class),
|
||||||
Balance (BalanceEffect.class),
|
Balance (BalanceEffect.class),
|
||||||
BecomeMonarch (BecomeMonarchEffect.class),
|
BecomeMonarch (BecomeMonarchEffect.class),
|
||||||
BecomesBlocked (BecomesBlockedEffect.class),
|
BecomesBlocked (BecomesBlockedEffect.class),
|
||||||
@@ -81,6 +82,7 @@ public enum ApiType {
|
|||||||
GenericChoice (ChooseGenericEffect.class),
|
GenericChoice (ChooseGenericEffect.class),
|
||||||
Goad (GoadEffect.class),
|
Goad (GoadEffect.class),
|
||||||
Haunt (HauntEffect.class),
|
Haunt (HauntEffect.class),
|
||||||
|
ImmediateTrigger (ImmediateTriggerEffect.class),
|
||||||
LookAt (LookAtEffect.class),
|
LookAt (LookAtEffect.class),
|
||||||
LoseLife (LifeLoseEffect.class),
|
LoseLife (LifeLoseEffect.class),
|
||||||
LosesGame (GameLossEffect.class),
|
LosesGame (GameLossEffect.class),
|
||||||
@@ -142,6 +144,7 @@ public enum ApiType {
|
|||||||
SkipTurn (SkipTurnEffect.class),
|
SkipTurn (SkipTurnEffect.class),
|
||||||
StoreSVar (StoreSVarEffect.class),
|
StoreSVar (StoreSVarEffect.class),
|
||||||
StoreMap (StoreMapEffect.class),
|
StoreMap (StoreMapEffect.class),
|
||||||
|
Surveil (SurveilEffect.class),
|
||||||
Tap (TapEffect.class),
|
Tap (TapEffect.class),
|
||||||
TapAll (TapAllEffect.class),
|
TapAll (TapAllEffect.class),
|
||||||
TapOrUntap (TapOrUntapEffect.class),
|
TapOrUntap (TapOrUntapEffect.class),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import forge.GameCommand;
|
||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
@@ -385,4 +386,83 @@ public abstract class SpellAbilityEffect {
|
|||||||
|
|
||||||
return eff;
|
return eff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static void replaceDying(final SpellAbility sa) {
|
||||||
|
if (sa.hasParam("ReplaceDyingDefined") || sa.hasParam("ReplaceDyingValid")) {
|
||||||
|
|
||||||
|
if (sa.hasParam("ReplaceDyingCondition")) {
|
||||||
|
// currently there is only one with Kicker
|
||||||
|
final String condition = sa.getParam("ReplaceDyingCondition");
|
||||||
|
if ("Kicked".equals(condition)) {
|
||||||
|
if (!sa.isKicked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Card host = sa.getHostCard();
|
||||||
|
final Player controller = sa.getActivatingPlayer();
|
||||||
|
final Game game = host.getGame();
|
||||||
|
String zone = sa.getParamOrDefault("ReplaceDyingZone", "Exile");
|
||||||
|
|
||||||
|
CardCollection cards = null;
|
||||||
|
|
||||||
|
if (sa.hasParam("ReplaceDyingDefined")) {
|
||||||
|
cards = AbilityUtils.getDefinedCards(host, sa.getParam("ReplaceDyingDefined"), sa);
|
||||||
|
// no cards, no need for Effect
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build an Effect with that infomation
|
||||||
|
String name = host.getName() + "'s Effect";
|
||||||
|
|
||||||
|
final Card eff = createEffect(host, controller, name, host.getImageKey());
|
||||||
|
if (cards != null) {
|
||||||
|
eff.addRemembered(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
String valid = sa.getParamOrDefault("ReplaceDyingValid", "Card.IsRemembered");
|
||||||
|
|
||||||
|
String repeffstr = "Event$ Moved | ValidCard$ " + valid +
|
||||||
|
"| Origin$ Battlefield | Destination$ Graveyard " +
|
||||||
|
"| Description$ If the creature would die this turn, exile it instead.";
|
||||||
|
String effect = "DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ " + zone;
|
||||||
|
|
||||||
|
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||||
|
re.setLayer(ReplacementLayer.Other);
|
||||||
|
|
||||||
|
re.setOverridingAbility(AbilityFactory.getAbility(effect, eff));
|
||||||
|
eff.addReplacementEffect(re);
|
||||||
|
|
||||||
|
if (cards != null) {
|
||||||
|
// Add forgot trigger
|
||||||
|
addForgetOnMovedTrigger(eff, "Battlefield");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy text changes
|
||||||
|
if (sa.isIntrinsic()) {
|
||||||
|
eff.copyChangedTextFrom(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
final GameCommand endEffect = new GameCommand() {
|
||||||
|
private static final long serialVersionUID = -5861759814760561373L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
game.getAction().exile(eff, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
game.getEndOfTurn().addUntil(endEffect);
|
||||||
|
|
||||||
|
eff.updateStateForView();
|
||||||
|
|
||||||
|
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||||
|
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||||
|
game.getAction().moveTo(ZoneType.Command, eff, sa);
|
||||||
|
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import forge.util.Lang;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -49,7 +50,8 @@ public class ActivateAbilityEffect extends SpellAbilityEffect {
|
|||||||
if (possibleAb.isEmpty()) {
|
if (possibleAb.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SpellAbility manaAb = p.getController().chooseSingleSpellForEffect(possibleAb, sa, "Choose a mana ability:");
|
SpellAbility manaAb = p.getController().chooseSingleSpellForEffect(
|
||||||
|
possibleAb, sa, "Choose a mana ability:", ImmutableMap.of());
|
||||||
p.getController().playChosenSpellAbility(manaAb);
|
p.getController().playChosenSpellAbility(manaAb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
public class AnimateAllEffect extends AnimateEffectBase {
|
public class AnimateAllEffect extends AnimateEffectBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -144,6 +146,9 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
list = CardLists.getValidCards(list, valid.split(","), host.getController(), host, sa);
|
list = CardLists.getValidCards(list, valid.split(","), host.getController(), host, sa);
|
||||||
|
|
||||||
|
boolean removeAll = sa.hasParam("RemoveAllAbilities");
|
||||||
|
boolean removeIntrinsic = sa.hasParam("RemoveIntrinsicAbilities");
|
||||||
|
|
||||||
for (final Card c : list) {
|
for (final Card c : list) {
|
||||||
doAnimate(c, sa, power, toughness, types, removeTypes, finalDesc,
|
doAnimate(c, sa, power, toughness, types, removeTypes, finalDesc,
|
||||||
keywords, removeKeywords, hiddenKeywords, timestamp);
|
keywords, removeKeywords, hiddenKeywords, timestamp);
|
||||||
@@ -161,14 +166,17 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
// remove abilities
|
// remove abilities
|
||||||
final List<SpellAbility> removedAbilities = new ArrayList<SpellAbility>();
|
final List<SpellAbility> removedAbilities = new ArrayList<SpellAbility>();
|
||||||
if (sa.hasParam("OverwriteAbilities") || sa.hasParam("RemoveAllAbilities")) {
|
if (sa.hasParam("OverwriteAbilities") || removeAll || removeIntrinsic) {
|
||||||
for (final SpellAbility ab : c.getSpellAbilities()) {
|
for (final SpellAbility ab : c.getSpellAbilities()) {
|
||||||
if (ab.isAbility()) {
|
if (ab.isAbility()) {
|
||||||
c.removeSpellAbility(ab);
|
if (removeAll
|
||||||
|
|| (ab.isIntrinsic() && removeIntrinsic && !ab.isBasicLandAbility())) {
|
||||||
|
ab.setTemporarilySuppressed(true);
|
||||||
removedAbilities.add(ab);
|
removedAbilities.add(ab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// give replacement effects
|
// give replacement effects
|
||||||
final List<ReplacementEffect> addedReplacements = new ArrayList<ReplacementEffect>();
|
final List<ReplacementEffect> addedReplacements = new ArrayList<ReplacementEffect>();
|
||||||
if (replacements.size() > 0) {
|
if (replacements.size() > 0) {
|
||||||
@@ -190,19 +198,24 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
// suppress triggers from the animated card
|
// suppress triggers from the animated card
|
||||||
final List<Trigger> removedTriggers = new ArrayList<Trigger>();
|
final List<Trigger> removedTriggers = new ArrayList<Trigger>();
|
||||||
if (sa.hasParam("OverwriteTriggers") || sa.hasParam("RemoveAllAbilities")) {
|
if (sa.hasParam("OverwriteTriggers") || removeAll || removeIntrinsic) {
|
||||||
final FCollectionView<Trigger> triggersToRemove = c.getTriggers();
|
final FCollectionView<Trigger> triggersToRemove = c.getTriggers();
|
||||||
for (final Trigger trigger : triggersToRemove) {
|
for (final Trigger trigger : triggersToRemove) {
|
||||||
trigger.setSuppressed(true);
|
if (removeIntrinsic && !trigger.isIntrinsic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
trigger.setSuppressed(true); // why this not TemporarilySuppressed?
|
||||||
removedTriggers.add(trigger);
|
removedTriggers.add(trigger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// suppress static abilities from the animated card
|
// suppress static abilities from the animated card
|
||||||
final List<StaticAbility> removedStatics = new ArrayList<StaticAbility>();
|
final List<StaticAbility> removedStatics = new ArrayList<StaticAbility>();
|
||||||
if (sa.hasParam("OverwriteStatics") || sa.hasParam("RemoveAllAbilities")) {
|
if (sa.hasParam("OverwriteStatics") || removeAll || removeIntrinsic) {
|
||||||
final FCollectionView<StaticAbility> staticsToRemove = c.getStaticAbilities();
|
for (final StaticAbility stAb : c.getStaticAbilities()) {
|
||||||
for (final StaticAbility stAb : staticsToRemove) {
|
if (removeIntrinsic && !stAb.isIntrinsic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
stAb.setTemporarilySuppressed(true);
|
stAb.setTemporarilySuppressed(true);
|
||||||
removedStatics.add(stAb);
|
removedStatics.add(stAb);
|
||||||
}
|
}
|
||||||
@@ -210,9 +223,11 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
// suppress static abilities from the animated card
|
// suppress static abilities from the animated card
|
||||||
final List<ReplacementEffect> removedReplacements = new ArrayList<ReplacementEffect>();
|
final List<ReplacementEffect> removedReplacements = new ArrayList<ReplacementEffect>();
|
||||||
if (sa.hasParam("OverwriteReplacements") || sa.hasParam("RemoveAllAbilities")) {
|
if (sa.hasParam("OverwriteReplacements") || removeAll || removeIntrinsic) {
|
||||||
final FCollectionView<ReplacementEffect> replacementsToRemove = c.getReplacementEffects();
|
for (final ReplacementEffect re : c.getReplacementEffects()) {
|
||||||
for (final ReplacementEffect re : replacementsToRemove) {
|
if (removeIntrinsic && !re.isIntrinsic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
re.setTemporarilySuppressed(true);
|
re.setTemporarilySuppressed(true);
|
||||||
removedReplacements.add(re);
|
removedReplacements.add(re);
|
||||||
}
|
}
|
||||||
@@ -234,8 +249,11 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
public void run() {
|
public void run() {
|
||||||
doUnanimate(c, sa, finalDesc, hiddenKeywords,
|
doUnanimate(c, sa, finalDesc, hiddenKeywords,
|
||||||
addedAbilities, addedTriggers, addedReplacements,
|
addedAbilities, addedTriggers, addedReplacements,
|
||||||
false, removedAbilities, timestamp);
|
ImmutableList.of(), timestamp);
|
||||||
|
|
||||||
|
for (final SpellAbility sa : removedAbilities) {
|
||||||
|
sa.setTemporarilySuppressed(false);
|
||||||
|
}
|
||||||
// give back suppressed triggers
|
// give back suppressed triggers
|
||||||
for (final Trigger t : removedTriggers) {
|
for (final Trigger t : removedTriggers) {
|
||||||
t.setSuppressed(false);
|
t.setSuppressed(false);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import forge.game.spellability.SpellAbility;
|
|||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerHandler;
|
import forge.game.trigger.TriggerHandler;
|
||||||
import forge.util.collect.FCollectionView;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -162,21 +161,20 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
boolean clearAbilities = sa.hasParam("OverwriteAbilities");
|
boolean clearAbilities = sa.hasParam("OverwriteAbilities");
|
||||||
boolean clearSpells = sa.hasParam("OverwriteSpells");
|
boolean clearSpells = sa.hasParam("OverwriteSpells");
|
||||||
boolean removeAll = sa.hasParam("RemoveAllAbilities");
|
boolean removeAll = sa.hasParam("RemoveAllAbilities");
|
||||||
|
boolean removeIntrinsic = sa.hasParam("RemoveIntrinsicAbilities");
|
||||||
|
|
||||||
if (clearAbilities || clearSpells || removeAll) {
|
if (clearAbilities || clearSpells || removeAll) {
|
||||||
for (final SpellAbility ab : c.getSpellAbilities()) {
|
for (final SpellAbility ab : c.getSpellAbilities()) {
|
||||||
if (removeAll || (ab.isAbility() && clearAbilities)
|
if (removeAll
|
||||||
|
|| (ab.isIntrinsic() && removeIntrinsic && !ab.isBasicLandAbility())
|
||||||
|
|| (ab.isAbility() && clearAbilities)
|
||||||
|| (ab.isSpell() && clearSpells)) {
|
|| (ab.isSpell() && clearSpells)) {
|
||||||
|
ab.setTemporarilySuppressed(true);
|
||||||
removedAbilities.add(ab);
|
removedAbilities.add(ab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't rmeove SAs in foreach loop that finds them
|
|
||||||
for (final SpellAbility ab : removedAbilities) {
|
|
||||||
c.removeSpellAbility(ab);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("RemoveThisAbility") && !removedAbilities.contains(sa)) {
|
if (sa.hasParam("RemoveThisAbility") && !removedAbilities.contains(sa)) {
|
||||||
c.removeSpellAbility(sa);
|
c.removeSpellAbility(sa);
|
||||||
removedAbilities.add(sa);
|
removedAbilities.add(sa);
|
||||||
@@ -215,20 +213,23 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
// suppress triggers from the animated card
|
// suppress triggers from the animated card
|
||||||
final List<Trigger> removedTriggers = Lists.newArrayList();
|
final List<Trigger> removedTriggers = Lists.newArrayList();
|
||||||
if (sa.hasParam("OverwriteTriggers") || removeAll) {
|
if (sa.hasParam("OverwriteTriggers") || removeAll || removeIntrinsic) {
|
||||||
final FCollectionView<Trigger> triggersToRemove = c.getTriggers();
|
for (final Trigger trigger : c.getTriggers()) {
|
||||||
for (final Trigger trigger : triggersToRemove) {
|
if (removeIntrinsic && !trigger.isIntrinsic()) {
|
||||||
trigger.setSuppressed(true);
|
continue;
|
||||||
|
}
|
||||||
|
trigger.setSuppressed(true); // why this not TemporarilySuppressed?
|
||||||
removedTriggers.add(trigger);
|
removedTriggers.add(trigger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// give static abilities (should only be used by cards to give
|
// give static abilities (should only be used by cards to give
|
||||||
// itself a static ability)
|
// itself a static ability)
|
||||||
|
final List<StaticAbility> addedStaticAbilities = Lists.newArrayList();
|
||||||
if (stAbs.size() > 0) {
|
if (stAbs.size() > 0) {
|
||||||
for (final String s : stAbs) {
|
for (final String s : stAbs) {
|
||||||
final String actualAbility = source.getSVar(s);
|
final String actualAbility = source.getSVar(s);
|
||||||
c.addStaticAbility(actualAbility);
|
addedStaticAbilities.add(c.addStaticAbility(actualAbility));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,9 +249,11 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
// suppress static abilities from the animated card
|
// suppress static abilities from the animated card
|
||||||
final List<StaticAbility> removedStatics = Lists.newArrayList();
|
final List<StaticAbility> removedStatics = Lists.newArrayList();
|
||||||
if (sa.hasParam("OverwriteStatics") || removeAll) {
|
if (sa.hasParam("OverwriteStatics") || removeAll || removeIntrinsic) {
|
||||||
final FCollectionView<StaticAbility> staticsToRemove = c.getStaticAbilities();
|
for (final StaticAbility stAb : c.getStaticAbilities()) {
|
||||||
for (final StaticAbility stAb : staticsToRemove) {
|
if (removeIntrinsic && !stAb.isIntrinsic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
stAb.setTemporarilySuppressed(true);
|
stAb.setTemporarilySuppressed(true);
|
||||||
removedStatics.add(stAb);
|
removedStatics.add(stAb);
|
||||||
}
|
}
|
||||||
@@ -258,8 +261,11 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
// suppress static abilities from the animated card
|
// suppress static abilities from the animated card
|
||||||
final List<ReplacementEffect> removedReplacements = Lists.newArrayList();
|
final List<ReplacementEffect> removedReplacements = Lists.newArrayList();
|
||||||
if (sa.hasParam("OverwriteReplacements") || removeAll) {
|
if (sa.hasParam("OverwriteReplacements") || removeAll || removeIntrinsic) {
|
||||||
for (final ReplacementEffect re : c.getReplacementEffects()) {
|
for (final ReplacementEffect re : c.getReplacementEffects()) {
|
||||||
|
if (removeIntrinsic && !re.isIntrinsic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
re.setTemporarilySuppressed(true);
|
re.setTemporarilySuppressed(true);
|
||||||
removedReplacements.add(re);
|
removedReplacements.add(re);
|
||||||
}
|
}
|
||||||
@@ -272,8 +278,6 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean givesStAbs = (stAbs.size() > 0);
|
|
||||||
|
|
||||||
final GameCommand unanimate = new GameCommand() {
|
final GameCommand unanimate = new GameCommand() {
|
||||||
private static final long serialVersionUID = -5861759814760561373L;
|
private static final long serialVersionUID = -5861759814760561373L;
|
||||||
|
|
||||||
@@ -281,9 +285,13 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
public void run() {
|
public void run() {
|
||||||
doUnanimate(c, sa, finalDesc, hiddenKeywords,
|
doUnanimate(c, sa, finalDesc, hiddenKeywords,
|
||||||
addedAbilities, addedTriggers, addedReplacements,
|
addedAbilities, addedTriggers, addedReplacements,
|
||||||
givesStAbs, removedAbilities, timestamp);
|
addedStaticAbilities, timestamp);
|
||||||
|
|
||||||
game.fireEvent(new GameEventCardStatsChanged(c));
|
game.fireEvent(new GameEventCardStatsChanged(c));
|
||||||
|
|
||||||
|
for (final SpellAbility sa : removedAbilities) {
|
||||||
|
sa.setTemporarilySuppressed(false);
|
||||||
|
}
|
||||||
// give back suppressed triggers
|
// give back suppressed triggers
|
||||||
for (final Trigger t : removedTriggers) {
|
for (final Trigger t : removedTriggers) {
|
||||||
t.setSuppressed(false);
|
t.setSuppressed(false);
|
||||||
@@ -458,6 +466,8 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
sb.append(" until ").append(host).append(" leaves the battlefield.");
|
sb.append(" until ").append(host).append(" leaves the battlefield.");
|
||||||
} else if (sa.hasParam("UntilYourNextUpkeep")) {
|
} else if (sa.hasParam("UntilYourNextUpkeep")) {
|
||||||
sb.append(" until your next upkeep.");
|
sb.append(" until your next upkeep.");
|
||||||
|
} else if (sa.hasParam("UntilYourNextTurn")) {
|
||||||
|
sb.append(" until your next turn.");
|
||||||
} else if (sa.hasParam("UntilControllerNextUntap")) {
|
} else if (sa.hasParam("UntilControllerNextUntap")) {
|
||||||
sb.append(" until its controller's next untap step.");
|
sb.append(" until its controller's next untap step.");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -24,11 +24,10 @@ import forge.game.replacement.ReplacementEffect;
|
|||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
||||||
void doAnimate(final Card c, final SpellAbility sa, final Integer power, final Integer toughness,
|
public static void doAnimate(final Card c, final SpellAbility sa, final Integer power, final Integer toughness,
|
||||||
final CardType addType, final CardType removeType, final String colors,
|
final CardType addType, final CardType removeType, final String colors,
|
||||||
final List<String> keywords, final List<String> removeKeywords,
|
final List<String> keywords, final List<String> removeKeywords,
|
||||||
final List<String> hiddenKeywords, final long timestamp) {
|
final List<String> hiddenKeywords, final long timestamp) {
|
||||||
@@ -36,15 +35,19 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
boolean removeSuperTypes = false;
|
boolean removeSuperTypes = false;
|
||||||
boolean removeCardTypes = false;
|
boolean removeCardTypes = false;
|
||||||
boolean removeSubTypes = false;
|
boolean removeSubTypes = false;
|
||||||
|
boolean removeLandTypes = false;
|
||||||
boolean removeCreatureTypes = false;
|
boolean removeCreatureTypes = false;
|
||||||
boolean removeArtifactTypes = false;
|
boolean removeArtifactTypes = false;
|
||||||
|
boolean removeEnchantmentTypes = false;
|
||||||
|
|
||||||
if (sa.hasParam("OverwriteTypes")) {
|
if (sa.hasParam("OverwriteTypes")) {
|
||||||
removeSuperTypes = true;
|
removeSuperTypes = true;
|
||||||
removeCardTypes = true;
|
removeCardTypes = true;
|
||||||
removeSubTypes = true;
|
removeSubTypes = true;
|
||||||
|
removeLandTypes = true;
|
||||||
removeCreatureTypes = true;
|
removeCreatureTypes = true;
|
||||||
removeArtifactTypes = true;
|
removeArtifactTypes = true;
|
||||||
|
removeEnchantmentTypes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("KeepSupertypes")) {
|
if (sa.hasParam("KeepSupertypes")) {
|
||||||
@@ -57,6 +60,10 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (sa.hasParam("KeepSubtypes")) {
|
if (sa.hasParam("KeepSubtypes")) {
|
||||||
removeSubTypes = false;
|
removeSubTypes = false;
|
||||||
|
removeLandTypes = false;
|
||||||
|
removeCreatureTypes = false;
|
||||||
|
removeArtifactTypes = false;
|
||||||
|
removeEnchantmentTypes = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("RemoveSuperTypes")) {
|
if (sa.hasParam("RemoveSuperTypes")) {
|
||||||
@@ -71,23 +78,30 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
removeSubTypes = true;
|
removeSubTypes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sa.hasParam("RemoveLandTypes")) {
|
||||||
|
removeCreatureTypes = true;
|
||||||
|
}
|
||||||
if (sa.hasParam("RemoveCreatureTypes")) {
|
if (sa.hasParam("RemoveCreatureTypes")) {
|
||||||
removeCreatureTypes = true;
|
removeCreatureTypes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("RemoveArtifactTypes")) {
|
if (sa.hasParam("RemoveArtifactTypes")) {
|
||||||
removeArtifactTypes = true;
|
removeArtifactTypes = true;
|
||||||
}
|
}
|
||||||
|
if (sa.hasParam("RemoveEnchantmentTypes")) {
|
||||||
|
removeEnchantmentTypes = true;
|
||||||
|
}
|
||||||
|
|
||||||
if ((power != null) || (toughness != null)) {
|
if ((power != null) || (toughness != null)) {
|
||||||
c.addNewPT(power, toughness, timestamp);
|
c.addNewPT(power, toughness, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
removeCreatureTypes, removeArtifactTypes, timestamp);
|
removeLandTypes, removeCreatureTypes, removeArtifactTypes, removeEnchantmentTypes, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.addChangedCardKeywords(keywords, removeKeywords, sa.hasParam("RemoveAllAbilities"), timestamp);
|
c.addChangedCardKeywords(keywords, removeKeywords,
|
||||||
|
sa.hasParam("RemoveAllAbilities"), sa.hasParam("RemoveIntrinsicAbilities"), timestamp);
|
||||||
|
|
||||||
for (final String k : hiddenKeywords) {
|
for (final String k : hiddenKeywords) {
|
||||||
c.addHiddenExtrinsicKeyword(k);
|
c.addHiddenExtrinsicKeyword(k);
|
||||||
@@ -114,10 +128,10 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
* @param timestamp
|
* @param timestamp
|
||||||
* a long.
|
* a long.
|
||||||
*/
|
*/
|
||||||
void doUnanimate(final Card c, SpellAbility sa, final String colorDesc,
|
static void doUnanimate(final Card c, SpellAbility sa, final String colorDesc,
|
||||||
final List<String> hiddenKeywords, final List<SpellAbility> addedAbilities,
|
final List<String> hiddenKeywords, final List<SpellAbility> addedAbilities,
|
||||||
final List<Trigger> addedTriggers, final List<ReplacementEffect> addedReplacements,
|
final List<Trigger> addedTriggers, final List<ReplacementEffect> addedReplacements,
|
||||||
final boolean givesStAbs, final List<SpellAbility> removedAbilities, final long timestamp) {
|
final List<StaticAbility> addedStaticAbilities, final long timestamp) {
|
||||||
|
|
||||||
if (sa.hasParam("LastsIndefinitely")) {
|
if (sa.hasParam("LastsIndefinitely")) {
|
||||||
return;
|
return;
|
||||||
@@ -127,16 +141,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
|
|
||||||
c.removeChangedCardKeywords(timestamp);
|
c.removeChangedCardKeywords(timestamp);
|
||||||
|
|
||||||
// remove all static abilities
|
|
||||||
if (givesStAbs) {
|
|
||||||
c.setStaticAbilities(new ArrayList<StaticAbility>());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("Types") || sa.hasParam("RemoveTypes")
|
|
||||||
|| sa.hasParam("RemoveCreatureTypes") || sa.hasParam("RemoveArtifactTypes")) {
|
|
||||||
c.removeChangedCardTypes(timestamp);
|
c.removeChangedCardTypes(timestamp);
|
||||||
}
|
|
||||||
|
|
||||||
c.removeColor(timestamp);
|
c.removeColor(timestamp);
|
||||||
|
|
||||||
for (final String k : hiddenKeywords) {
|
for (final String k : hiddenKeywords) {
|
||||||
@@ -147,10 +152,6 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
c.removeSpellAbility(saAdd);
|
c.removeSpellAbility(saAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final SpellAbility saRem : removedAbilities) {
|
|
||||||
c.addSpellAbility(saRem);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Trigger t : addedTriggers) {
|
for (final Trigger t : addedTriggers) {
|
||||||
c.removeTrigger(t);
|
c.removeTrigger(t);
|
||||||
}
|
}
|
||||||
@@ -159,6 +160,10 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
c.removeReplacementEffect(rep);
|
c.removeReplacementEffect(rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (final StaticAbility stAb : addedStaticAbilities) {
|
||||||
|
c.removeStaticAbility(stAb);
|
||||||
|
}
|
||||||
|
|
||||||
// any other unanimate cleanup
|
// any other unanimate cleanup
|
||||||
if (!c.isCreature()) {
|
if (!c.isCreature()) {
|
||||||
c.unEquipAllCards();
|
c.unEquipAllCards();
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
import forge.game.Game;
|
||||||
|
import forge.game.GameObject;
|
||||||
|
import forge.game.ability.AbilityUtils;
|
||||||
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
|
public class AssignGroupEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.game.ability.SpellAbilityEffect#getStackDescription(forge.game.spellability.SpellAbility)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
|
return sa.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see forge.game.ability.SpellAbilityEffect#resolve(forge.game.spellability.SpellAbility)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void resolve(SpellAbility sa) {
|
||||||
|
final Card host = sa.getHostCard();
|
||||||
|
final Game game = host.getGame();
|
||||||
|
|
||||||
|
List<GameObject> defined = getDefinedOrTargeted(sa, "Defined");
|
||||||
|
|
||||||
|
final List<SpellAbility> abilities = Lists.<SpellAbility>newArrayList(sa.getAdditionalAbilityList("Choices"));
|
||||||
|
|
||||||
|
Player chooser = sa.getActivatingPlayer();
|
||||||
|
if (sa.hasParam("Chooser")) {
|
||||||
|
final String choose = sa.getParam("Chooser");
|
||||||
|
chooser = AbilityUtils.getDefinedPlayers(sa.getHostCard(), choose, sa).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Multimap<SpellAbility, GameObject> result = ArrayListMultimap.create();
|
||||||
|
|
||||||
|
for (GameObject g : defined) {
|
||||||
|
final String title = "Choose ability for " + g.toString();
|
||||||
|
Map<String, Object> params = Maps.newHashMap();
|
||||||
|
params.put("Affected", g);
|
||||||
|
|
||||||
|
result.put(chooser.getController().chooseSingleSpellForEffect(abilities, sa, title, params), g);
|
||||||
|
}
|
||||||
|
|
||||||
|
// in order of choice list
|
||||||
|
for (SpellAbility s : abilities) {
|
||||||
|
// is that in Player order?
|
||||||
|
Collection<GameObject> l = result.get(s);
|
||||||
|
|
||||||
|
host.addRemembered(l);
|
||||||
|
AbilityUtils.resolve(s);
|
||||||
|
host.removeRemembered(l);
|
||||||
|
|
||||||
|
// this will refresh continuous abilities for players and permanents.
|
||||||
|
game.getAction().checkStaticAbilities();
|
||||||
|
game.getTriggerHandler().resetActiveTriggers(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -156,7 +156,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
|||||||
// Auras without Candidates stay in their current location
|
// Auras without Candidates stay in their current location
|
||||||
if (c.isAura()) {
|
if (c.isAura()) {
|
||||||
final SpellAbility saAura = c.getFirstAttachSpell();
|
final SpellAbility saAura = c.getFirstAttachSpell();
|
||||||
if (!saAura.getTargetRestrictions().hasCandidates(saAura, false)) {
|
if (saAura != null && !saAura.getTargetRestrictions().hasCandidates(saAura, false)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import com.google.common.collect.Iterables;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import forge.GameCommand;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
|
import forge.card.CardType;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
@@ -32,6 +34,7 @@ import forge.util.collect.*;
|
|||||||
import forge.util.Lang;
|
import forge.util.Lang;
|
||||||
import forge.util.MessageUtil;
|
import forge.util.MessageUtil;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -484,7 +487,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
if (sa.hasParam("WithCounters")) {
|
if (sa.hasParam("WithCounters")) {
|
||||||
String[] parse = sa.getParam("WithCounters").split("_");
|
String[] parse = sa.getParam("WithCounters").split("_");
|
||||||
tgtC.addEtbCounter(CounterType.getType(parse[0]), Integer.parseInt(parse[1]), hostCard);
|
tgtC.addEtbCounter(CounterType.getType(parse[0]), Integer.parseInt(parse[1]), player);
|
||||||
}
|
}
|
||||||
if (sa.hasParam("GainControl")) {
|
if (sa.hasParam("GainControl")) {
|
||||||
if (sa.hasParam("NewController")) {
|
if (sa.hasParam("NewController")) {
|
||||||
@@ -552,11 +555,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
// location
|
// location
|
||||||
if (tgtC.isAura()) {
|
if (tgtC.isAura()) {
|
||||||
final SpellAbility saAura = tgtC.getFirstAttachSpell();
|
final SpellAbility saAura = tgtC.getFirstAttachSpell();
|
||||||
|
if (saAura != null) {
|
||||||
saAura.setActivatingPlayer(sa.getActivatingPlayer());
|
saAura.setActivatingPlayer(sa.getActivatingPlayer());
|
||||||
if (!saAura.getTargetRestrictions().hasCandidates(saAura, false)) {
|
if (!saAura.getTargetRestrictions().hasCandidates(saAura, false)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
movedCard = game.getAction().moveTo(
|
movedCard = game.getAction().moveTo(
|
||||||
tgtC.getController().getZone(destination), tgtC, sa, null);
|
tgtC.getController().getZone(destination), tgtC, sa, null);
|
||||||
@@ -865,7 +870,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
final boolean champion = sa.hasParam("Champion");
|
final boolean champion = sa.hasParam("Champion");
|
||||||
final boolean forget = sa.hasParam("ForgetChanged");
|
final boolean forget = sa.hasParam("ForgetChanged");
|
||||||
final boolean imprint = sa.hasParam("Imprint");
|
final boolean imprint = sa.hasParam("Imprint");
|
||||||
final String selectPrompt = sa.hasParam("SelectPrompt") ? sa.getParam("SelectPrompt") : MessageUtil.formatMessage("Select a card from {player's} " + Lang.joinHomogenous(origin).toLowerCase(), decider, player);
|
String selectPrompt = sa.hasParam("SelectPrompt") ? sa.getParam("SelectPrompt") : MessageUtil.formatMessage("Select a card from {player's} " + Lang.joinHomogenous(origin).toLowerCase(), decider, player);
|
||||||
final String totalcmc = sa.getParam("WithTotalCMC");
|
final String totalcmc = sa.getParam("WithTotalCMC");
|
||||||
int totcmc = AbilityUtils.calculateAmount(source, totalcmc, sa);
|
int totcmc = AbilityUtils.calculateAmount(source, totalcmc, sa);
|
||||||
|
|
||||||
@@ -874,7 +879,20 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
CardCollection chosenCards = new CardCollection();
|
CardCollection chosenCards = new CardCollection();
|
||||||
// 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)) {
|
||||||
for (Card card : decider.getController().chooseCardsForZoneChange(destination, origin, sa, fetchList, delayedReveal, selectPrompt, decider)) {
|
List<Card> selectedCards;
|
||||||
|
if (! sa.hasParam("SelectPrompt")) {
|
||||||
|
// new default messaging for multi select
|
||||||
|
if (fetchList.size() > changeNum) {
|
||||||
|
selectPrompt = MessageUtil.formatMessage("Select up to " + changeNum + " cards from {player's} " + Lang.joinHomogenous(origin).toLowerCase(), decider, player);
|
||||||
|
} else {
|
||||||
|
selectPrompt = MessageUtil.formatMessage("Select cards from {player's} " + Lang.joinHomogenous(origin).toLowerCase(), decider, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ensure that selection is within maximum allowed changeNum
|
||||||
|
do {
|
||||||
|
selectedCards = decider.getController().chooseCardsForZoneChange(destination, origin, sa, fetchList, delayedReveal, selectPrompt, decider);
|
||||||
|
} while (selectedCards != null && selectedCards.size() > changeNum);
|
||||||
|
for (Card card : selectedCards) {
|
||||||
chosenCards.add(card);
|
chosenCards.add(card);
|
||||||
};
|
};
|
||||||
// maybe prompt the user if they selected fewer than the maximum possible?
|
// maybe prompt the user if they selected fewer than the maximum possible?
|
||||||
@@ -1101,11 +1119,49 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger
|
||||||
|
if (sa.hasParam("FaceDown") && ZoneType.Battlefield.equals(destination)) {
|
||||||
|
c.setState(CardStateName.FaceDown, true);
|
||||||
|
|
||||||
|
// set New Pt doesn't work because this values need to be copyable for clone effects
|
||||||
|
if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness")) {
|
||||||
|
if (sa.hasParam("FaceDownPower")) {
|
||||||
|
c.setBasePower(AbilityUtils.calculateAmount(
|
||||||
|
source, sa.getParam("FaceDownPower"), sa));
|
||||||
|
}
|
||||||
|
if (sa.hasParam("FaceDownToughness")) {
|
||||||
|
c.setBaseToughness(AbilityUtils.calculateAmount(
|
||||||
|
source, sa.getParam("FaceDownToughness"), sa));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sa.hasParam("FaceDownAddType")) {
|
||||||
|
CardType t = new CardType(c.getCurrentState().getType());
|
||||||
|
t.addAll(Arrays.asList(sa.getParam("FaceDownAddType").split(",")));
|
||||||
|
c.getCurrentState().setType(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness")
|
||||||
|
|| sa.hasParam("FaceDownAddType")) {
|
||||||
|
final GameCommand unanimate = new GameCommand() {
|
||||||
|
private static final long serialVersionUID = 8853789549297846163L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
c.clearStates(CardStateName.FaceDown, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
c.addFaceupCommand(unanimate);
|
||||||
|
}
|
||||||
|
}
|
||||||
movedCard = game.getAction().moveTo(c.getController().getZone(destination), c, sa, null);
|
movedCard = game.getAction().moveTo(c.getController().getZone(destination), c, sa, null);
|
||||||
if (sa.hasParam("Tapped")) {
|
if (sa.hasParam("Tapped")) {
|
||||||
movedCard.setTapped(true);
|
movedCard.setTapped(true);
|
||||||
}
|
}
|
||||||
if (sa.hasParam("FaceDown")) {
|
|
||||||
|
// need to do that again?
|
||||||
|
if (sa.hasParam("FaceDown") && !ZoneType.Battlefield.equals(destination)) {
|
||||||
movedCard.setState(CardStateName.FaceDown, true);
|
movedCard.setState(CardStateName.FaceDown, true);
|
||||||
}
|
}
|
||||||
movedCard.setTimestamp(ts);
|
movedCard.setTimestamp(ts);
|
||||||
@@ -1181,7 +1237,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
&& !sa.hasParam("DifferentNames")
|
&& !sa.hasParam("DifferentNames")
|
||||||
&& !sa.hasParam("DifferentCMC")
|
&& !sa.hasParam("DifferentCMC")
|
||||||
&& !sa.hasParam("AtRandom")
|
&& !sa.hasParam("AtRandom")
|
||||||
&& !sa.hasParam("ChangeNum") // TODO: doesn't work with card number limits, e.g. Doomsday
|
|
||||||
&& (!sa.hasParam("Defined") || sa.hasParam("ChooseFromDefined"))
|
&& (!sa.hasParam("Defined") || sa.hasParam("ChooseFromDefined"))
|
||||||
&& sa.getParam("WithTotalCMC") == null;
|
&& sa.getParam("WithTotalCMC") == null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,17 +105,8 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
|||||||
final String message = validDesc.equals("card") ? "Name a card" : "Name a " + validDesc + " card.";
|
final String message = validDesc.equals("card") ? "Name a card" : "Name a " + validDesc + " card.";
|
||||||
|
|
||||||
Predicate<ICardFace> cpp = Predicates.alwaysTrue();
|
Predicate<ICardFace> cpp = Predicates.alwaysTrue();
|
||||||
if ( StringUtils.containsIgnoreCase(valid, "nonland") ) {
|
if (sa.hasParam("ValidCards")) {
|
||||||
cpp = CardFacePredicates.Presets.IS_NON_LAND;
|
cpp = CardFacePredicates.valid(valid);
|
||||||
}
|
|
||||||
if ( StringUtils.containsIgnoreCase(valid, "nonbasic") ) {
|
|
||||||
cpp = Predicates.not(CardFacePredicates.Presets.IS_BASIC_LAND);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( StringUtils.containsIgnoreCase(valid, "noncreature") ) {
|
|
||||||
cpp = Predicates.not(CardFacePredicates.Presets.IS_CREATURE);
|
|
||||||
} else if ( StringUtils.containsIgnoreCase(valid, "creature") ) {
|
|
||||||
cpp = CardFacePredicates.Presets.IS_CREATURE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chosen = p.getController().chooseCardName(sa, cpp, valid, message);
|
chosen = p.getController().chooseCardName(sa, cpp, valid, message);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
@@ -65,7 +66,8 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
|
|||||||
int idxChosen = MyRandom.getRandom().nextInt(abilities.size());
|
int idxChosen = MyRandom.getRandom().nextInt(abilities.size());
|
||||||
chosenSA = abilities.get(idxChosen);
|
chosenSA = abilities.get(idxChosen);
|
||||||
} else {
|
} else {
|
||||||
chosenSA = p.getController().chooseSingleSpellForEffect(abilities, sa, "Choose one");
|
chosenSA = p.getController().chooseSingleSpellForEffect(abilities, sa, "Choose one",
|
||||||
|
ImmutableMap.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chosenSA != null) {
|
if (chosenSA != null) {
|
||||||
|
|||||||
@@ -64,11 +64,15 @@ public class CloneEffect extends SpellAbilityEffect {
|
|||||||
Card cardToCopy = null;
|
Card cardToCopy = null;
|
||||||
|
|
||||||
if (sa.hasParam("Choices")) {
|
if (sa.hasParam("Choices")) {
|
||||||
CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
|
ZoneType choiceZone = ZoneType.Battlefield;
|
||||||
|
if (sa.hasParam("ChoiceZone")) {
|
||||||
|
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
|
||||||
|
}
|
||||||
|
CardCollectionView choices = game.getCardsIn(choiceZone);
|
||||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
||||||
|
|
||||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card ";
|
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card ";
|
||||||
cardToCopy = activator.getController().chooseSingleEntityForEffect(choices, sa, title, true);
|
cardToCopy = activator.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
||||||
} else if (sa.hasParam("Defined")) {
|
} else if (sa.hasParam("Defined")) {
|
||||||
List<Card> cloneSources = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
List<Card> cloneSources = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
||||||
if (!cloneSources.isEmpty()) {
|
if (!cloneSources.isEmpty()) {
|
||||||
@@ -213,6 +217,9 @@ public class CloneEffect extends SpellAbilityEffect {
|
|||||||
else if (duration.equals("UntilYourNextTurn")) {
|
else if (duration.equals("UntilYourNextTurn")) {
|
||||||
game.getCleanup().addUntil(host.getController(), unclone);
|
game.getCleanup().addUntil(host.getController(), unclone);
|
||||||
}
|
}
|
||||||
|
else if (duration.equals("UntilUnattached")) {
|
||||||
|
sa.getHostCard().addUnattachCommand(unclone);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
game.fireEvent(new GameEventCardStatsChanged(tgtCard));
|
game.fireEvent(new GameEventCardStatsChanged(tgtCard));
|
||||||
} // cloneResolve
|
} // cloneResolve
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import com.google.common.base.Predicates;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
|
|
||||||
import forge.ImageKeys;
|
import forge.ImageKeys;
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
@@ -22,6 +21,7 @@ import forge.game.card.CardCollectionView;
|
|||||||
import forge.game.card.CardFactory;
|
import forge.game.card.CardFactory;
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardFactoryUtil;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
|
import forge.game.card.token.TokenInfo;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.event.GameEventCombatChanged;
|
import forge.game.event.GameEventCombatChanged;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
@@ -37,10 +37,10 @@ import forge.util.collect.FCollectionView;
|
|||||||
import forge.util.PredicateString.StringOp;
|
import forge.util.PredicateString.StringOp;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class CopyPermanentEffect extends SpellAbilityEffect {
|
public class CopyPermanentEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -167,12 +167,18 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (sa.hasParam("Choices")) {
|
} else if (sa.hasParam("Choices")) {
|
||||||
|
Player chooser = activator;
|
||||||
|
if (sa.hasParam("Chooser")) {
|
||||||
|
final String choose = sa.getParam("Chooser");
|
||||||
|
chooser = AbilityUtils.getDefinedPlayers(sa.getHostCard(), choose, sa).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
|
||||||
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
|
||||||
if (!choices.isEmpty()) {
|
if (!choices.isEmpty()) {
|
||||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card ";
|
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : "Choose a card ";
|
||||||
|
|
||||||
Card choosen = activator.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false);
|
||||||
|
|
||||||
if (choosen != null) {
|
if (choosen != null) {
|
||||||
tgtCards.add(choosen);
|
tgtCards.add(choosen);
|
||||||
@@ -186,29 +192,17 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
|||||||
for (final Card c : tgtCards) {
|
for (final Card c : tgtCards) {
|
||||||
if (!sa.usesTargeting() || c.canBeTargetedBy(sa)) {
|
if (!sa.usesTargeting() || c.canBeTargetedBy(sa)) {
|
||||||
|
|
||||||
int multiplier = numCopies;
|
Pair<Player, Integer> result = TokenInfo.calculateMultiplier(
|
||||||
|
game, controller, true, numCopies);
|
||||||
|
|
||||||
final Map<String, Object> repParams = Maps.newHashMap();
|
if (result.getRight() <= 0) {
|
||||||
repParams.put("Event", "CreateToken");
|
|
||||||
repParams.put("Affected", controller);
|
|
||||||
repParams.put("TokenNum", multiplier);
|
|
||||||
repParams.put("EffectOnly", true);
|
|
||||||
|
|
||||||
switch (game.getReplacementHandler().run(repParams)) {
|
|
||||||
case NotReplaced:
|
|
||||||
break;
|
|
||||||
case Updated: {
|
|
||||||
multiplier = (int) repParams.get("TokenNum");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Card> crds = Lists.newArrayListWithCapacity(multiplier);
|
final List<Card> crds = Lists.newArrayListWithCapacity(result.getRight());
|
||||||
|
|
||||||
for (int i = 0; i < multiplier; i++) {
|
for (int i = 0; i < result.getRight(); i++) {
|
||||||
final Card copy = CardFactory.copyCopiableCharacteristics(c, activator);
|
final Card copy = CardFactory.copyCopiableCharacteristics(c, result.getLeft());
|
||||||
copy.setToken(true);
|
copy.setToken(true);
|
||||||
copy.setCopiedPermanent(c);
|
copy.setCopiedPermanent(c);
|
||||||
// add keywords from sa
|
// add keywords from sa
|
||||||
@@ -337,7 +331,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set the controller before move to play: Crafty Cutpurse
|
// set the controller before move to play: Crafty Cutpurse
|
||||||
copy.setController(controller, 0);
|
copy.setController(result.getLeft(), 0);
|
||||||
copy.updateStateForView();
|
copy.updateStateForView();
|
||||||
|
|
||||||
// Temporarily register triggers of an object created with CopyPermanent
|
// Temporarily register triggers of an object created with CopyPermanent
|
||||||
@@ -350,7 +344,7 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
|
|||||||
copyInPlay.setCloneOrigin(host);
|
copyInPlay.setCloneOrigin(host);
|
||||||
sa.getHostCard().addClone(copyInPlay);
|
sa.getHostCard().addClone(copyInPlay);
|
||||||
if (!pumpKeywords.isEmpty()) {
|
if (!pumpKeywords.isEmpty()) {
|
||||||
copyInPlay.addChangedCardKeywords(pumpKeywords, Lists.<String>newArrayList(), false, timestamp);
|
copyInPlay.addChangedCardKeywords(pumpKeywords, Lists.<String>newArrayList(), false, false, timestamp);
|
||||||
}
|
}
|
||||||
crds.add(copyInPlay);
|
crds.add(copyInPlay);
|
||||||
if (sa.hasParam("RememberCopied")) {
|
if (sa.hasParam("RememberCopied")) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
@@ -83,8 +84,9 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
for (int multi = 0; multi < spellCount && !tgtSpells.isEmpty(); multi++) {
|
for (int multi = 0; multi < spellCount && !tgtSpells.isEmpty(); multi++) {
|
||||||
String prompt = "Select " + Lang.getOrdinal(multi + 1) + " spell to copy to stack";
|
String prompt = "Select " + Lang.getOrdinal(multi + 1) + " spell to copy to stack";
|
||||||
SpellAbility chosen = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa, prompt);
|
SpellAbility chosen = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa, prompt,
|
||||||
SpellAbility copiedSpell = CardFactory.copySpellAbilityAndSrcCard(card, chosen.getHostCard(), chosen, true);
|
ImmutableMap.of());
|
||||||
|
SpellAbility copiedSpell = CardFactory.copySpellAbilityAndPossiblyHost(card, chosen.getHostCard(), chosen, true);
|
||||||
copiedSpell.getHostCard().setController(card.getController(), card.getGame().getNextTimestamp());
|
copiedSpell.getHostCard().setController(card.getController(), card.getGame().getNextTimestamp());
|
||||||
copiedSpell.setActivatingPlayer(controller);
|
copiedSpell.setActivatingPlayer(controller);
|
||||||
copies.add(copiedSpell);
|
copies.add(copiedSpell);
|
||||||
@@ -92,7 +94,8 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (sa.hasParam("CopyForEachCanTarget")) {
|
else if (sa.hasParam("CopyForEachCanTarget")) {
|
||||||
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa, "Select a spell to copy");
|
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa,
|
||||||
|
"Select a spell to copy", ImmutableMap.of());
|
||||||
chosenSA.setActivatingPlayer(controller);
|
chosenSA.setActivatingPlayer(controller);
|
||||||
// Find subability or rootability that has targets
|
// Find subability or rootability that has targets
|
||||||
SpellAbility targetedSA = chosenSA;
|
SpellAbility targetedSA = chosenSA;
|
||||||
@@ -114,7 +117,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
mayChooseNewTargets = false;
|
mayChooseNewTargets = false;
|
||||||
for (GameEntity o : candidates) {
|
for (GameEntity o : candidates) {
|
||||||
SpellAbility copy = CardFactory.copySpellAbilityAndSrcCard(card, chosenSA.getHostCard(), chosenSA, true);
|
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true);
|
||||||
resetFirstTargetOnCopy(copy, o, targetedSA);
|
resetFirstTargetOnCopy(copy, o, targetedSA);
|
||||||
copies.add(copy);
|
copies.add(copy);
|
||||||
}
|
}
|
||||||
@@ -140,22 +143,23 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
valid.remove(originalTarget);
|
valid.remove(originalTarget);
|
||||||
mayChooseNewTargets = false;
|
mayChooseNewTargets = false;
|
||||||
for (final Card c : valid) {
|
for (final Card c : valid) {
|
||||||
SpellAbility copy = CardFactory.copySpellAbilityAndSrcCard(card, chosenSA.getHostCard(), chosenSA, true);
|
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true);
|
||||||
resetFirstTargetOnCopy(copy, c, targetedSA);
|
resetFirstTargetOnCopy(copy, c, targetedSA);
|
||||||
copies.add(copy);
|
copies.add(copy);
|
||||||
}
|
}
|
||||||
for (final Player p : players) {
|
for (final Player p : players) {
|
||||||
SpellAbility copy = CardFactory.copySpellAbilityAndSrcCard(card, chosenSA.getHostCard(), chosenSA, true);
|
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true);
|
||||||
resetFirstTargetOnCopy(copy, p, targetedSA);
|
resetFirstTargetOnCopy(copy, p, targetedSA);
|
||||||
copies.add(copy);
|
copies.add(copy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa, "Select a spell to copy");
|
SpellAbility chosenSA = controller.getController().chooseSingleSpellForEffect(tgtSpells, sa,
|
||||||
|
"Select a spell to copy", ImmutableMap.of());
|
||||||
chosenSA.setActivatingPlayer(controller);
|
chosenSA.setActivatingPlayer(controller);
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
copies.add(CardFactory.copySpellAbilityAndSrcCard(card, chosenSA.getHostCard(), chosenSA, true));
|
copies.add(CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
// Test to see if the card we're trying to add is in the expected state
|
// Test to see if the card we're trying to add is in the expected state
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
dest = cur;
|
||||||
|
|
||||||
int csum = 0;
|
int csum = 0;
|
||||||
|
|
||||||
@@ -145,7 +146,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (csum > 0) {
|
if (csum > 0) {
|
||||||
dest.addCounter(cType, csum, host, true);
|
dest.addCounter(cType, csum, player, true);
|
||||||
game.updateLastStateForCard(dest);
|
game.updateLastStateForCard(dest);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -194,15 +195,15 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
Map<String, Object> params = Maps.newHashMap();
|
Map<String, Object> params = Maps.newHashMap();
|
||||||
params.put("CounterType", cType);
|
params.put("CounterType", cType);
|
||||||
params.put("Source", source);
|
params.put("Source", source);
|
||||||
params.put("Target", dest);
|
params.put("Target", cur);
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Put how many ").append(cType.getName()).append(" counters on ").append(dest).append("?");
|
sb.append("Put how many ").append(cType.getName()).append(" counters on ").append(cur).append("?");
|
||||||
int cnum = player.getController().chooseNumber(sa, sb.toString(), 0, source.getCounters(cType), params);
|
int cnum = player.getController().chooseNumber(sa, sb.toString(), 0, source.getCounters(cType), params);
|
||||||
|
|
||||||
if (cnum > 0) {
|
if (cnum > 0) {
|
||||||
source.subtractCounter(cType, cnum);
|
source.subtractCounter(cType, cnum);
|
||||||
dest.addCounter(cType, cnum, host, true);
|
cur.addCounter(cType, cnum, player, true);
|
||||||
game.updateLastStateForCard(dest);
|
game.updateLastStateForCard(cur);
|
||||||
updateSource = true;
|
updateSource = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,7 +246,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!"Any".matches(counterName)) {
|
if (!"Any".matches(counterName)) {
|
||||||
if (!dest.canReceiveCounters(cType)) {
|
if (!cur.canReceiveCounters(cType)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +254,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
Map<String, Object> params = Maps.newHashMap();
|
Map<String, Object> params = Maps.newHashMap();
|
||||||
params.put("CounterType", cType);
|
params.put("CounterType", cType);
|
||||||
params.put("Source", source);
|
params.put("Source", source);
|
||||||
params.put("Target", dest);
|
params.put("Target", cur);
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Take how many ").append(cType.getName());
|
sb.append("Take how many ").append(cType.getName());
|
||||||
sb.append(" counters from ").append(source).append("?");
|
sb.append(" counters from ").append(source).append("?");
|
||||||
@@ -262,8 +263,8 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (source.getCounters(cType) >= cntToMove) {
|
if (source.getCounters(cType) >= cntToMove) {
|
||||||
source.subtractCounter(cType, cntToMove);
|
source.subtractCounter(cType, cntToMove);
|
||||||
dest.addCounter(cType, cntToMove, host, true);
|
cur.addCounter(cType, cntToMove, player, true);
|
||||||
game.updateLastStateForCard(dest);
|
game.updateLastStateForCard(cur);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// any counterType currently only Leech Bonder
|
// any counterType currently only Leech Bonder
|
||||||
@@ -296,7 +297,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
sa, sb.toString(), 0, Math.min(tgtCounters.get(chosenType), cntToMove), params);
|
sa, sb.toString(), 0, Math.min(tgtCounters.get(chosenType), cntToMove), params);
|
||||||
|
|
||||||
if (chosenAmount > 0) {
|
if (chosenAmount > 0) {
|
||||||
dest.addCounter(chosenType, chosenAmount, host, true);
|
dest.addCounter(chosenType, chosenAmount, player, true);
|
||||||
source.subtractCounter(chosenType, chosenAmount);
|
source.subtractCounter(chosenType, chosenAmount);
|
||||||
game.updateLastStateForCard(dest);
|
game.updateLastStateForCard(dest);
|
||||||
cntToMove -= chosenAmount;
|
cntToMove -= chosenAmount;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ 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;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.Lang;
|
import forge.util.Lang;
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ public class CountersMultiplyEffect extends SpellAbilityEffect {
|
|||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final Game game = host.getGame();
|
final Game game = host.getGame();
|
||||||
|
final Player player = sa.getActivatingPlayer();
|
||||||
|
|
||||||
final CounterType counterType = getCounterType(sa);
|
final CounterType counterType = getCounterType(sa);
|
||||||
final int n = Integer.valueOf(sa.getParamOrDefault("Multiplier", "2")) - 1;
|
final int n = Integer.valueOf(sa.getParamOrDefault("Multiplier", "2")) - 1;
|
||||||
@@ -50,10 +52,10 @@ public class CountersMultiplyEffect extends SpellAbilityEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (counterType != null) {
|
if (counterType != null) {
|
||||||
gameCard.addCounter(counterType, gameCard.getCounters(counterType) * n, host, true);
|
gameCard.addCounter(counterType, gameCard.getCounters(counterType) * n, player, true);
|
||||||
} 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, host, true);
|
gameCard.addCounter(e.getKey(), e.getValue() * n, player, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(gameCard);
|
game.updateLastStateForCard(gameCard);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class CountersNoteEffect extends SpellAbilityEffect {
|
public class CountersNoteEffect extends SpellAbilityEffect {
|
||||||
@@ -26,7 +27,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);
|
loadCounters(c, source, sa.getActivatingPlayer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,11 +40,11 @@ public class CountersNoteEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadCounters(Card notee, Card source) {
|
private void loadCounters(Card notee, Card source, final Player p) {
|
||||||
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(CounterType.getType(key.substring(NOTE_COUNTERS.length())), Integer.parseInt(svar.getValue()), source, false);
|
notee.addCounter(CounterType.getType(key.substring(NOTE_COUNTERS.length())), Integer.parseInt(svar.getValue()), p, false);
|
||||||
}
|
}
|
||||||
// TODO Probably should "remove" the svars that were temporarily used
|
// TODO Probably should "remove" the svars that were temporarily used
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
|
final Player p = sa.getActivatingPlayer();
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final Game game = host.getGame();
|
final Game game = host.getGame();
|
||||||
Player controller = host.getController();
|
Player controller = host.getController();
|
||||||
@@ -32,10 +33,10 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
|
|||||||
return;
|
return;
|
||||||
for(Entry<GameEntity, CounterType> ge: proliferateChoice.entrySet()) {
|
for(Entry<GameEntity, CounterType> ge: proliferateChoice.entrySet()) {
|
||||||
if( ge.getKey() instanceof Player )
|
if( ge.getKey() instanceof Player )
|
||||||
((Player) ge.getKey()).addCounter(ge.getValue(), 1, host, true);
|
((Player) ge.getKey()).addCounter(ge.getValue(), 1, p, true);
|
||||||
else if( ge.getKey() instanceof Card) {
|
else if( ge.getKey() instanceof Card) {
|
||||||
Card c = (Card) ge.getKey();
|
Card c = (Card) ge.getKey();
|
||||||
c.addCounter(ge.getValue(), 1, host, true);
|
c.addCounter(ge.getValue(), 1, p, true);
|
||||||
game.updateLastStateForCard(c);
|
game.updateLastStateForCard(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,30 +38,37 @@ public class CountersPutAllEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
final Player activator = sa.getActivatingPlayer();
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final int counterAmount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
|
final int counterAmount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
|
||||||
final String valid = sa.getParam("ValidCards");
|
final String valid = sa.getParam("ValidCards");
|
||||||
final ZoneType zone = sa.hasParam("ValidZone") ? ZoneType.smartValueOf(sa.getParam("ValidZone")) : ZoneType.Battlefield;
|
final ZoneType zone = sa.hasParam("ValidZone") ? ZoneType.smartValueOf(sa.getParam("ValidZone")) : ZoneType.Battlefield;
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
final Game game = activator.getGame();
|
||||||
|
|
||||||
if (counterAmount <= 0) {
|
if (counterAmount <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollectionView cards = game.getCardsIn(zone);
|
CardCollectionView cards = game.getCardsIn(zone);
|
||||||
cards = CardLists.getValidCards(cards, valid, sa.getHostCard().getController(), sa.getHostCard());
|
cards = CardLists.getValidCards(cards, valid, host.getController(), sa.getHostCard());
|
||||||
|
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
final Player pl = sa.getTargets().getFirstTargetedPlayer();
|
final Player pl = sa.getTargets().getFirstTargetedPlayer();
|
||||||
cards = CardLists.filterControlledBy(cards, pl);
|
cards = CardLists.filterControlledBy(cards, pl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player placer = activator;
|
||||||
|
if (sa.hasParam("Placer")) {
|
||||||
|
final String pstr = sa.getParam("Placer");
|
||||||
|
placer = AbilityUtils.getDefinedPlayers(host, pstr, sa).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
for (final Card tgtCard : cards) {
|
for (final Card tgtCard : cards) {
|
||||||
if (game.getZoneOf(tgtCard).is(ZoneType.Battlefield)) {
|
if (game.getZoneOf(tgtCard).is(ZoneType.Battlefield)) {
|
||||||
tgtCard.addCounter(CounterType.valueOf(type), counterAmount, host, true);
|
tgtCard.addCounter(CounterType.valueOf(type), counterAmount, placer, true);
|
||||||
} else {
|
} else {
|
||||||
// adding counters to something like re-suspend cards
|
// adding counters to something like re-suspend cards
|
||||||
tgtCard.addCounter(CounterType.valueOf(type), counterAmount, host, false);
|
tgtCard.addCounter(CounterType.valueOf(type), counterAmount, placer, false);
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(tgtCard);
|
game.updateLastStateForCard(tgtCard);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
final boolean dividedAsYouChoose = sa.hasParam("DividedAsYouChoose");
|
final boolean dividedAsYouChoose = sa.hasParam("DividedAsYouChoose");
|
||||||
|
|
||||||
|
|
||||||
final int amount = AbilityUtils.calculateAmount(card, sa.getParam("CounterNum"), sa);
|
final int amount = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("CounterNum", "1"), sa);
|
||||||
if (sa.hasParam("Bolster")) {
|
if (sa.hasParam("Bolster")) {
|
||||||
sb.append("Bolster ").append(amount);
|
sb.append("Bolster ").append(amount);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
@@ -110,6 +110,12 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player placer = activator;
|
||||||
|
if (sa.hasParam("Placer")) {
|
||||||
|
final String pstr = sa.getParam("Placer");
|
||||||
|
placer = AbilityUtils.getDefinedPlayers(sa.getHostCard(), pstr, sa).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
final boolean etbcounter = sa.hasParam("ETB");
|
final boolean etbcounter = sa.hasParam("ETB");
|
||||||
final boolean remember = sa.hasParam("RememberCounters");
|
final boolean remember = sa.hasParam("RememberCounters");
|
||||||
final boolean rememberCards = sa.hasParam("RememberCards");
|
final boolean rememberCards = sa.hasParam("RememberCards");
|
||||||
@@ -129,9 +135,10 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
for (final GameObject obj : tgtObjects) {
|
for (final GameObject obj : tgtObjects) {
|
||||||
// check if the object is still in game or if it was moved
|
// check if the object is still in game or if it was moved
|
||||||
|
Card gameCard = null;
|
||||||
if (obj instanceof Card) {
|
if (obj instanceof Card) {
|
||||||
Card tgtCard = (Card) obj;
|
Card tgtCard = (Card) obj;
|
||||||
Card gameCard = game.getCardState(tgtCard, null);
|
gameCard = game.getCardState(tgtCard, null);
|
||||||
// gameCard is LKI in that case, the card is not in game anymore
|
// gameCard is LKI in that case, the card is not in game anymore
|
||||||
// or the timestamp did change
|
// or the timestamp did change
|
||||||
// this should check Self too
|
// this should check Self too
|
||||||
@@ -155,10 +162,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, card, true);
|
((Player) obj).addCounter(ct, counterAmount, placer, true);
|
||||||
}
|
}
|
||||||
if (obj instanceof Card) {
|
if (obj instanceof Card) {
|
||||||
((Card) obj).addCounter(ct, counterAmount, card, true);
|
gameCard.addCounter(ct, counterAmount, placer, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -179,7 +186,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (obj instanceof Card) {
|
if (obj instanceof Card) {
|
||||||
Card tgtCard = (Card) obj;
|
Card tgtCard = gameCard;
|
||||||
counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(tgtCard) : counterAmount;
|
counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(tgtCard) : counterAmount;
|
||||||
if (!sa.usesTargeting() || tgtCard.canBeTargetedBy(sa)) {
|
if (!sa.usesTargeting() || tgtCard.canBeTargetedBy(sa)) {
|
||||||
if (max != -1) {
|
if (max != -1) {
|
||||||
@@ -232,9 +239,9 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
final Zone zone = tgtCard.getGame().getZoneOf(tgtCard);
|
final Zone zone = tgtCard.getGame().getZoneOf(tgtCard);
|
||||||
if (zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack)) {
|
if (zone == null || zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Stack)) {
|
||||||
if (etbcounter) {
|
if (etbcounter) {
|
||||||
tgtCard.addEtbCounter(counterType, counterAmount, card);
|
tgtCard.addEtbCounter(counterType, counterAmount, placer);
|
||||||
} else {
|
} else {
|
||||||
tgtCard.addCounter(counterType, counterAmount, card, true);
|
tgtCard.addCounter(counterType, counterAmount, placer, true);
|
||||||
}
|
}
|
||||||
if (remember) {
|
if (remember) {
|
||||||
final int value = tgtCard.getTotalCountersToAdd();
|
final int value = tgtCard.getTotalCountersToAdd();
|
||||||
@@ -263,9 +270,9 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
// adding counters to something like re-suspend cards
|
// adding counters to something like re-suspend cards
|
||||||
// etbcounter should apply multiplier
|
// etbcounter should apply multiplier
|
||||||
if (etbcounter) {
|
if (etbcounter) {
|
||||||
tgtCard.addEtbCounter(counterType, counterAmount, card);
|
tgtCard.addEtbCounter(counterType, counterAmount, placer);
|
||||||
} else {
|
} else {
|
||||||
tgtCard.addCounter(counterType, counterAmount, card, false);
|
tgtCard.addCounter(counterType, counterAmount, placer, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(tgtCard);
|
game.updateLastStateForCard(tgtCard);
|
||||||
@@ -273,7 +280,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, card, true);
|
pl.addCounter(counterType, counterAmount, placer, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerController;
|
import forge.game.player.PlayerController;
|
||||||
import forge.game.player.PlayerController.BinaryChoiceType;
|
import forge.game.player.PlayerController.BinaryChoiceType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -64,16 +65,16 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
|||||||
if (gameCard == null || !tgtCard.equalsWithTimestamp(gameCard)) {
|
if (gameCard == null || !tgtCard.equalsWithTimestamp(gameCard)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!sa.usesTargeting() || tgtCard.canBeTargetedBy(sa)) {
|
if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) {
|
||||||
if (tgtCard.hasCounters()) {
|
if (gameCard.hasCounters()) {
|
||||||
if (sa.hasParam("EachExistingCounter")) {
|
if (sa.hasParam("EachExistingCounter")) {
|
||||||
for (CounterType listType : Lists.newArrayList(tgtCard.getCounters().keySet())) {
|
for (CounterType listType : Lists.newArrayList(gameCard.getCounters().keySet())) {
|
||||||
addOrRemoveCounter(sa, tgtCard, listType, counterAmount);
|
addOrRemoveCounter(sa, gameCard, listType, counterAmount);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addOrRemoveCounter(sa, tgtCard, ctype, counterAmount);
|
addOrRemoveCounter(sa, gameCard, ctype, counterAmount);
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(tgtCard);
|
game.updateLastStateForCard(gameCard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,8 +82,8 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
private void addOrRemoveCounter(final SpellAbility sa, final Card tgtCard, CounterType ctype,
|
private void addOrRemoveCounter(final SpellAbility sa, final Card tgtCard, CounterType ctype,
|
||||||
final int counterAmount) {
|
final int counterAmount) {
|
||||||
PlayerController pc = sa.getActivatingPlayer().getController();
|
final Player pl = sa.getActivatingPlayer();
|
||||||
final Card source = sa.getHostCard();
|
final PlayerController pc = pl.getController();
|
||||||
|
|
||||||
Map<String, Object> params = Maps.newHashMap();
|
Map<String, Object> params = Maps.newHashMap();
|
||||||
params.put("Target", tgtCard);
|
params.put("Target", tgtCard);
|
||||||
@@ -105,7 +106,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, source, apply);
|
tgtCard.addCounter(chosenType, counterAmount, pl, apply);
|
||||||
} else {
|
} else {
|
||||||
tgtCard.subtractCounter(chosenType, counterAmount);
|
tgtCard.subtractCounter(chosenType, counterAmount);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,27 +115,27 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
if (gameCard == null || !tgtCard.equalsWithTimestamp(gameCard)) {
|
if (gameCard == null || !tgtCard.equalsWithTimestamp(gameCard)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!sa.usesTargeting() || tgtCard.canBeTargetedBy(sa)) {
|
if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) {
|
||||||
final Zone zone = game.getZoneOf(gameCard);
|
final Zone zone = game.getZoneOf(gameCard);
|
||||||
if (type.equals("All")) {
|
if (type.equals("All")) {
|
||||||
for (Map.Entry<CounterType, Integer> e : tgtCard.getCounters().entrySet()) {
|
for (Map.Entry<CounterType, Integer> e : gameCard.getCounters().entrySet()) {
|
||||||
tgtCard.subtractCounter(e.getKey(), e.getValue());
|
gameCard.subtractCounter(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(tgtCard);
|
game.updateLastStateForCard(gameCard);
|
||||||
continue;
|
continue;
|
||||||
} else if (num.equals("All")) {
|
} else if (num.equals("All")) {
|
||||||
cntToRemove = tgtCard.getCounters(counterType);
|
cntToRemove = gameCard.getCounters(counterType);
|
||||||
} else if (sa.getParam("CounterNum").equals("Remembered")) {
|
} else if (sa.getParam("CounterNum").equals("Remembered")) {
|
||||||
cntToRemove = tgtCard.getCountersAddedBy(card, counterType);
|
cntToRemove = gameCard.getCountersAddedBy(card, counterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerController pc = sa.getActivatingPlayer().getController();
|
PlayerController pc = sa.getActivatingPlayer().getController();
|
||||||
|
|
||||||
if (type.equals("Any")) {
|
if (type.equals("Any")) {
|
||||||
while (cntToRemove > 0 && tgtCard.hasCounters()) {
|
while (cntToRemove > 0 && gameCard.hasCounters()) {
|
||||||
final Map<CounterType, Integer> tgtCounters = tgtCard.getCounters();
|
final Map<CounterType, Integer> tgtCounters = gameCard.getCounters();
|
||||||
Map<String, Object> params = Maps.newHashMap();
|
Map<String, Object> params = Maps.newHashMap();
|
||||||
params.put("Target", tgtCard);
|
params.put("Target", gameCard);
|
||||||
|
|
||||||
String prompt = "Select type of counters to remove";
|
String prompt = "Select type of counters to remove";
|
||||||
CounterType chosenType = pc.chooseCounterType(
|
CounterType chosenType = pc.chooseCounterType(
|
||||||
@@ -143,13 +143,13 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
prompt = "Select the number of " + chosenType.getName() + " counters to remove";
|
prompt = "Select the number of " + chosenType.getName() + " counters to remove";
|
||||||
int max = Math.min(cntToRemove, tgtCounters.get(chosenType));
|
int max = Math.min(cntToRemove, tgtCounters.get(chosenType));
|
||||||
params = Maps.newHashMap();
|
params = Maps.newHashMap();
|
||||||
params.put("Target", tgtCard);
|
params.put("Target", gameCard);
|
||||||
params.put("CounterType", chosenType);
|
params.put("CounterType", chosenType);
|
||||||
int chosenAmount = pc.chooseNumber(sa, prompt, 1, max, params);
|
int chosenAmount = pc.chooseNumber(sa, prompt, 1, max, params);
|
||||||
|
|
||||||
if (chosenAmount > 0) {
|
if (chosenAmount > 0) {
|
||||||
tgtCard.subtractCounter(chosenType, chosenAmount);
|
gameCard.subtractCounter(chosenType, chosenAmount);
|
||||||
game.updateLastStateForCard(tgtCard);
|
game.updateLastStateForCard(gameCard);
|
||||||
if (rememberRemoved) {
|
if (rememberRemoved) {
|
||||||
for (int i = 0; i < chosenAmount; i++) {
|
for (int i = 0; i < chosenAmount; i++) {
|
||||||
card.addRemembered(Pair.of(chosenType, i));
|
card.addRemembered(Pair.of(chosenType, i));
|
||||||
@@ -159,12 +159,12 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cntToRemove = Math.min(cntToRemove, tgtCard.getCounters(counterType));
|
cntToRemove = Math.min(cntToRemove, gameCard.getCounters(counterType));
|
||||||
|
|
||||||
if (zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Exile)) {
|
if (zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Exile)) {
|
||||||
if (sa.hasParam("UpTo")) {
|
if (sa.hasParam("UpTo")) {
|
||||||
Map<String, Object> params = Maps.newHashMap();
|
Map<String, Object> params = Maps.newHashMap();
|
||||||
params.put("Target", tgtCard);
|
params.put("Target", gameCard);
|
||||||
params.put("CounterType", type);
|
params.put("CounterType", type);
|
||||||
String title = "Select the number of " + type + " counters to remove";
|
String title = "Select the number of " + type + " counters to remove";
|
||||||
cntToRemove = pc.chooseNumber(sa, title, 0, cntToRemove, params);
|
cntToRemove = pc.chooseNumber(sa, title, 0, cntToRemove, params);
|
||||||
@@ -172,13 +172,13 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
}
|
}
|
||||||
if (cntToRemove > 0) {
|
if (cntToRemove > 0) {
|
||||||
tgtCard.subtractCounter(counterType, cntToRemove);
|
gameCard.subtractCounter(counterType, cntToRemove);
|
||||||
if (rememberRemoved) {
|
if (rememberRemoved) {
|
||||||
for (int i = 0; i < cntToRemove; i++) {
|
for (int i = 0; i < cntToRemove; i++) {
|
||||||
card.addRemembered(Pair.of(counterType, i));
|
card.addRemembered(Pair.of(counterType, i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(tgtCard);
|
game.updateLastStateForCard(gameCard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +1,7 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import forge.GameCommand;
|
|
||||||
import forge.game.Game;
|
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
import forge.game.ability.AbilityUtils;
|
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
|
||||||
import forge.game.card.CardCollection;
|
|
||||||
import forge.game.player.Player;
|
|
||||||
import forge.game.replacement.ReplacementEffect;
|
|
||||||
import forge.game.replacement.ReplacementHandler;
|
|
||||||
import forge.game.replacement.ReplacementLayer;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
|
||||||
import forge.game.trigger.TriggerType;
|
|
||||||
import forge.game.zone.ZoneType;
|
|
||||||
|
|
||||||
abstract public class DamageBaseEffect extends SpellAbilityEffect {
|
abstract public class DamageBaseEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
static void replaceDying(final SpellAbility sa) {
|
|
||||||
if (sa.hasParam("ReplaceDyingDefined")) {
|
|
||||||
|
|
||||||
if (sa.hasParam("ReplaceDyingCondition")) {
|
|
||||||
// currently there is only one with Kicker
|
|
||||||
final String condition = sa.getParam("ReplaceDyingCondition");
|
|
||||||
if ("Kicked".equals(condition)) {
|
|
||||||
if (!sa.isKicked()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Card host = sa.getHostCard();
|
|
||||||
final Player controller = sa.getActivatingPlayer();
|
|
||||||
final Game game = host.getGame();
|
|
||||||
String zone = sa.getParamOrDefault("ReplaceDyingZone", "Exile");
|
|
||||||
CardCollection cards = AbilityUtils.getDefinedCards(host, sa.getParam("ReplaceDyingDefined"), sa);
|
|
||||||
// no cards, no need for Effect
|
|
||||||
if (cards.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// build an Effect with that infomation
|
|
||||||
String name = host.getName() + "'s Effect";
|
|
||||||
|
|
||||||
final Card eff = createEffect(host, controller, name, host.getImageKey());
|
|
||||||
eff.addRemembered(cards);
|
|
||||||
|
|
||||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered " +
|
|
||||||
"| Origin$ Battlefield | Destination$ Graveyard " +
|
|
||||||
"| Description$ If the creature would die this turn, exile it instead.";
|
|
||||||
String effect = "DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ " + zone;
|
|
||||||
|
|
||||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
|
||||||
re.setLayer(ReplacementLayer.Other);
|
|
||||||
|
|
||||||
re.setOverridingAbility(AbilityFactory.getAbility(effect, eff));
|
|
||||||
eff.addReplacementEffect(re);
|
|
||||||
|
|
||||||
// Add forgot trigger
|
|
||||||
addForgetOnMovedTrigger(eff, "Battlefield");
|
|
||||||
|
|
||||||
// Copy text changes
|
|
||||||
if (sa.isIntrinsic()) {
|
|
||||||
eff.copyChangedTextFrom(host);
|
|
||||||
}
|
|
||||||
|
|
||||||
final GameCommand endEffect = new GameCommand() {
|
|
||||||
private static final long serialVersionUID = -5861759814760561373L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
game.getAction().exile(eff, null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
game.getEndOfTurn().addUntil(endEffect);
|
|
||||||
|
|
||||||
eff.updateStateForView();
|
|
||||||
|
|
||||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
|
||||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
|
||||||
game.getAction().moveTo(ZoneType.Command, eff, sa);
|
|
||||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import forge.game.Game;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -68,6 +70,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card hostCard = sa.getHostCard();
|
final Card hostCard = sa.getHostCard();
|
||||||
|
final Game game = hostCard.getGame();
|
||||||
|
|
||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
int dmg = AbilityUtils.calculateAmount(hostCard, damage, sa);
|
int dmg = AbilityUtils.calculateAmount(hostCard, damage, sa);
|
||||||
@@ -176,7 +179,12 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
dmg = (sa.usesTargeting() && sa.hasParam("DividedAsYouChoose")) ? sa.getTargetRestrictions().getDividedValue(o) : dmg;
|
dmg = (sa.usesTargeting() && sa.hasParam("DividedAsYouChoose")) ? sa.getTargetRestrictions().getDividedValue(o) : dmg;
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card) {
|
||||||
final Card c = (Card) o;
|
final Card c = (Card) o;
|
||||||
if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) {
|
final Card gc = game.getCardState(c, null);
|
||||||
|
if (gc == null || !c.equalsWithTimestamp(gc) || !gc.isInPlay()) {
|
||||||
|
// timestamp different or not in play
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!targeted || c.canBeTargetedBy(sa)) {
|
||||||
if (removeDamage) {
|
if (removeDamage) {
|
||||||
c.setDamage(0);
|
c.setDamage(0);
|
||||||
c.setHasBeenDealtDeathtouchDamage(false);
|
c.setHasBeenDealtDeathtouchDamage(false);
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ public class DebuffEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removedKW.addAll(kws);
|
removedKW.addAll(kws);
|
||||||
tgtC.addChangedCardKeywords(addedKW, removedKW, false, timestamp);
|
tgtC.addChangedCardKeywords(addedKW, removedKW, false, false, timestamp);
|
||||||
}
|
}
|
||||||
if (!sa.hasParam("Permanent")) {
|
if (!sa.hasParam("Permanent")) {
|
||||||
game.getEndOfTurn().addUntil(new GameCommand() {
|
game.getEndOfTurn().addUntil(new GameCommand() {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class DigEffect extends SpellAbilityEffect {
|
|||||||
sb.append(Lang.nounWithAmount(numToDig, "card")).append(" of ");
|
sb.append(Lang.nounWithAmount(numToDig, "card")).append(" of ");
|
||||||
|
|
||||||
if (tgtPlayers.contains(host.getController())) {
|
if (tgtPlayers.contains(host.getController())) {
|
||||||
sb.append("his or her ");
|
sb.append("their ");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (final Player p : tgtPlayers) {
|
for (final Player p : tgtPlayers) {
|
||||||
@@ -377,7 +377,7 @@ public class DigEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
} else if (destZone2 == ZoneType.Exile) {
|
} else if (destZone2 == ZoneType.Exile) {
|
||||||
if (sa.hasParam("ExileWithCounter")) {
|
if (sa.hasParam("ExileWithCounter")) {
|
||||||
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")), 1, effectHost, true);
|
c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")), 1, player, true);
|
||||||
}
|
}
|
||||||
c.setExiledWith(effectHost);
|
c.setExiledWith(effectHost);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
|||||||
sb.append(pl).append(" ");
|
sb.append(pl).append(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("reveals cards from his or her library until revealing ");
|
sb.append("reveals cards from their library until revealing ");
|
||||||
sb.append(untilAmount).append(" ").append(desc).append(" card");
|
sb.append(untilAmount).append(" ").append(desc).append(" card");
|
||||||
if (untilAmount != 1) {
|
if (untilAmount != 1) {
|
||||||
sb.append("s");
|
sb.append("s");
|
||||||
@@ -56,18 +56,18 @@ public class DigUntilEffect extends SpellAbilityEffect {
|
|||||||
sb.append(" ");
|
sb.append(" ");
|
||||||
|
|
||||||
if (found.equals(ZoneType.Hand)) {
|
if (found.equals(ZoneType.Hand)) {
|
||||||
sb.append("into his or her hand ");
|
sb.append("into their hand ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (revealed.equals(ZoneType.Graveyard)) {
|
if (revealed.equals(ZoneType.Graveyard)) {
|
||||||
sb.append("and all other cards into his or her graveyard.");
|
sb.append("and all other cards into their graveyard.");
|
||||||
}
|
}
|
||||||
if (revealed.equals(ZoneType.Exile)) {
|
if (revealed.equals(ZoneType.Exile)) {
|
||||||
sb.append("and exile all other cards revealed this way.");
|
sb.append("and exile all other cards revealed this way.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (revealed.equals(ZoneType.Hand)) {
|
if (revealed.equals(ZoneType.Hand)) {
|
||||||
sb.append("all cards revealed this way into his or her hand");
|
sb.append("all cards revealed this way into their hand");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ public class DiscardEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mode.equals("RevealYouChoose")) {
|
if (mode.equals("RevealYouChoose")) {
|
||||||
sb.append("reveals his or her hand.").append(" You choose (");
|
sb.append("reveals their hand.").append(" You choose (");
|
||||||
} else if (mode.equals("RevealDiscardAll")) {
|
} else if (mode.equals("RevealDiscardAll")) {
|
||||||
sb.append("reveals his or her hand. Discard (");
|
sb.append("reveals their hand. Discard (");
|
||||||
} else {
|
} else {
|
||||||
sb.append("discards (");
|
sb.append("discards (");
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ public class DiscardEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mode.equals("Hand")) {
|
if (mode.equals("Hand")) {
|
||||||
sb.append("his or her hand");
|
sb.append("their hand");
|
||||||
} else if (mode.equals("RevealDiscardAll")) {
|
} else if (mode.equals("RevealDiscardAll")) {
|
||||||
sb.append("All");
|
sb.append("All");
|
||||||
} else if (sa.hasParam("AnyNumber")) {
|
} else if (sa.hasParam("AnyNumber")) {
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ public class ExploreEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
// check if only the activating player counts
|
// check if only the activating player counts
|
||||||
final Card card = sa.getHostCard();
|
|
||||||
final Player pl = sa.getActivatingPlayer();
|
final Player pl = sa.getActivatingPlayer();
|
||||||
final PlayerController pc = pl.getController();
|
final PlayerController pc = pl.getController();
|
||||||
final Game game = pl.getGame();
|
final Game game = pl.getGame();
|
||||||
@@ -78,7 +77,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.getTimestamp() == c.getTimestamp()) {
|
if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.getTimestamp() == c.getTimestamp()) {
|
||||||
c.addCounter(CounterType.P1P1, 1, card, true);
|
c.addCounter(CounterType.P1P1, 1, pl, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
List<Card> fighters = getFighters(sa);
|
List<Card> fighters = getFighters(sa);
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
final Game game = host.getGame();
|
||||||
|
|
||||||
if (fighters.size() < 2 || !fighters.get(0).isInPlay()
|
// check is done in getFighters
|
||||||
|| !fighters.get(1).isInPlay()) {
|
if (fighters.size() < 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,21 +55,7 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean fightToughness = sa.hasParam("FightWithToughness");
|
dealDamage(sa, fighters.get(0), fighters.get(1));
|
||||||
CardDamageMap damageMap = new CardDamageMap();
|
|
||||||
CardDamageMap preventMap = new CardDamageMap();
|
|
||||||
|
|
||||||
// Damage is dealt simultaneously, so we calculate the damage from source to target before it is applied
|
|
||||||
final int dmg1 = fightToughness ? fighters.get(0).getNetToughness() : fighters.get(0).getNetPower();
|
|
||||||
final int dmg2 = fightToughness ? fighters.get(1).getNetToughness() : fighters.get(1).getNetPower();
|
|
||||||
|
|
||||||
dealDamage(fighters.get(0), fighters.get(1), dmg1, damageMap, preventMap, sa);
|
|
||||||
dealDamage(fighters.get(1), fighters.get(0), dmg2, damageMap, preventMap, sa);
|
|
||||||
|
|
||||||
preventMap.triggerPreventDamage(false);
|
|
||||||
damageMap.triggerDamageDoneOnce(false, sa);
|
|
||||||
|
|
||||||
replaceDying(sa);
|
|
||||||
|
|
||||||
for (Card c : fighters) {
|
for (Card c : fighters) {
|
||||||
final Map<String, Object> runParams = Maps.newHashMap();
|
final Map<String, Object> runParams = Maps.newHashMap();
|
||||||
@@ -83,6 +69,8 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
|
|
||||||
Card fighter1 = null;
|
Card fighter1 = null;
|
||||||
Card fighter2 = null;
|
Card fighter2 = null;
|
||||||
|
final Card host = sa.getHostCard();
|
||||||
|
final Game game = host.getGame();
|
||||||
|
|
||||||
List<Card> tgts = null;
|
List<Card> tgts = null;
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
@@ -92,12 +80,27 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sa.hasParam("Defined")) {
|
if (sa.hasParam("Defined")) {
|
||||||
List<Card> defined = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
|
List<Card> defined = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
|
||||||
// Allow both fighters to come from defined list if first fighter not already found
|
// Allow both fighters to come from defined list if first fighter not already found
|
||||||
if (sa.hasParam("ExtraDefined")) {
|
if (sa.hasParam("ExtraDefined")) {
|
||||||
defined.addAll(AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("ExtraDefined"), sa));
|
defined.addAll(AbilityUtils.getDefinedCards(host, sa.getParam("ExtraDefined"), sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Card> newDefined = Lists.newArrayList();
|
||||||
|
for (final Card d : defined) {
|
||||||
|
final Card g = game.getCardState(d, null);
|
||||||
|
// 701.12b If a creature instructed to fight is no longer on the battlefield or is no longer a creature,
|
||||||
|
// no damage is dealt. If a creature is an illegal target
|
||||||
|
// for a resolving spell or ability that instructs it to fight, no damage is dealt.
|
||||||
|
if (g == null || !g.equalsWithTimestamp(d) || !d.isInPlay() || !d.isCreature()) {
|
||||||
|
// Test to see if the card we're trying to add is in the expected state
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newDefined.add(g);
|
||||||
|
}
|
||||||
|
// replace with new List using CardState
|
||||||
|
defined = newDefined;
|
||||||
|
|
||||||
if (!defined.isEmpty()) {
|
if (!defined.isEmpty()) {
|
||||||
if (defined.size() > 1 && fighter1 == null) {
|
if (defined.size() > 1 && fighter1 == null) {
|
||||||
fighter1 = defined.get(0);
|
fighter1 = defined.get(0);
|
||||||
@@ -121,8 +124,37 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
return fighterList;
|
return fighterList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dealDamage(Card source, Card target, int damage, CardDamageMap damageMap, CardDamageMap preventMap, final SpellAbility sa) {
|
private void dealDamage(final SpellAbility sa, Card fighterA, Card fighterB) {
|
||||||
target.addDamage(damage, source, damageMap, preventMap, sa);
|
boolean fightToughness = sa.hasParam("FightWithToughness");
|
||||||
|
|
||||||
|
boolean usedDamageMap = true;
|
||||||
|
CardDamageMap damageMap = sa.getDamageMap();
|
||||||
|
CardDamageMap preventMap = sa.getPreventMap();
|
||||||
|
|
||||||
|
if (damageMap == null) {
|
||||||
|
// make a new damage map
|
||||||
|
damageMap = new CardDamageMap();
|
||||||
|
preventMap = new CardDamageMap();
|
||||||
|
usedDamageMap = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 701.12c If a creature fights itself, it deals damage to itself equal to twice its power.
|
||||||
|
|
||||||
|
final int dmg1 = fightToughness ? fighterA.getNetToughness() : fighterA.getNetPower();
|
||||||
|
if (fighterA.equals(fighterB)) {
|
||||||
|
fighterA.addDamage(dmg1 * 2, fighterA, damageMap, preventMap, sa);
|
||||||
|
} else {
|
||||||
|
final int dmg2 = fightToughness ? fighterB.getNetToughness() : fighterB.getNetPower();
|
||||||
|
|
||||||
|
fighterB.addDamage(dmg1, fighterA, damageMap, preventMap, sa);
|
||||||
|
fighterA.addDamage(dmg2, fighterB, damageMap, preventMap, sa);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usedDamageMap) {
|
||||||
|
preventMap.triggerPreventDamage(false);
|
||||||
|
damageMap.triggerDamageDoneOnce(false, sa);
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceDying(sa);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,9 +118,9 @@ public class FlipCoinEffect extends SpellAbilityEffect {
|
|||||||
if (sa.getParam("RememberWinner") != null) {
|
if (sa.getParam("RememberWinner") != null) {
|
||||||
host.addRemembered(host);
|
host.addRemembered(host);
|
||||||
}
|
}
|
||||||
AbilitySub sub = sa.getAdditionalAbility("WinSubAbility");
|
|
||||||
if (sub != null) {
|
if (sa.hasAdditionalAbility("WinSubAbility")) {
|
||||||
AbilityUtils.resolve(sub);
|
AbilityUtils.resolve(sa.getAdditionalAbility("WinSubAbility"));
|
||||||
}
|
}
|
||||||
// runParams.put("Won","True");
|
// runParams.put("Won","True");
|
||||||
} else {
|
} else {
|
||||||
@@ -128,9 +128,8 @@ public class FlipCoinEffect extends SpellAbilityEffect {
|
|||||||
host.addRemembered(host);
|
host.addRemembered(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
AbilitySub sub = sa.getAdditionalAbility("LoseSubAbility");
|
if (sa.hasAdditionalAbility("LoseSubAbility")) {
|
||||||
if (sub != null) {
|
AbilityUtils.resolve(sa.getAdditionalAbility("LoseSubAbility"));
|
||||||
AbilityUtils.resolve(sub);
|
|
||||||
}
|
}
|
||||||
// runParams.put("Won","False");
|
// runParams.put("Won","False");
|
||||||
}
|
}
|
||||||
@@ -167,7 +166,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
|
|||||||
flipper.getGame().getAction().nofityOfValue(sa, flipper, result ? "heads" : "tails", null);
|
flipper.getGame().getAction().nofityOfValue(sa, flipper, result ? "heads" : "tails", null);
|
||||||
} while (sa.hasParam("FlipUntilYouLose") && result != false);
|
} while (sa.hasParam("FlipUntilYouLose") && result != false);
|
||||||
|
|
||||||
if (sa.hasParam("FlipUntilYouLose")) {
|
if (sa.hasParam("FlipUntilYouLose") && sa.hasAdditionalAbility("LoseSubAbility")) {
|
||||||
sa.getAdditionalAbility("LoseSubAbility").setSVar(varName, "Number$" + numSuccesses);
|
sa.getAdditionalAbility("LoseSubAbility").setSVar(varName, "Number$" + numSuccesses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +215,7 @@ public class FlipCoinEffect extends SpellAbilityEffect {
|
|||||||
caller.getGame().getTriggerHandler().runTrigger(TriggerType.FlippedCoin, runParams, false);
|
caller.getGame().getTriggerHandler().runTrigger(TriggerType.FlippedCoin, runParams, false);
|
||||||
} while (sa.hasParam("FlipUntilYouLose") && wonFlip);
|
} while (sa.hasParam("FlipUntilYouLose") && wonFlip);
|
||||||
|
|
||||||
if (sa.hasParam("FlipUntilYouLose")) {
|
if (sa.hasParam("FlipUntilYouLose") && sa.hasAdditionalAbility("LoseSubAbility")) {
|
||||||
sa.getAdditionalAbility("LoseSubAbility").setSVar(varName, "Number$" + numSuccesses);
|
sa.getAdditionalAbility("LoseSubAbility").setSVar(varName, "Number$" + numSuccesses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import forge.game.Game;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -8,10 +9,14 @@ public class HauntEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card card = sa.getHostCard();
|
Card card = sa.getHostCard();
|
||||||
if (sa.usesTargeting() && !card.isToken()) {
|
final Game game = card.getGame();
|
||||||
|
card = game.getCardState(card, null);
|
||||||
|
if (card == null) {
|
||||||
|
return;
|
||||||
|
} else if (sa.usesTargeting() && !card.isToken()) {
|
||||||
// haunt target but only if card is no token
|
// haunt target but only if card is no token
|
||||||
final Card copy = card.getGame().getAction().exile(card, sa);
|
final Card copy = game.getAction().exile(card, sa);
|
||||||
sa.getTargets().getFirstTargetedCard().addHauntedBy(copy);
|
sa.getTargets().getFirstTargetedCard().addHauntedBy(copy);
|
||||||
} else if (!sa.usesTargeting() && card.getHaunting() != null) {
|
} else if (!sa.usesTargeting() && card.getHaunting() != null) {
|
||||||
// unhaunt
|
// unhaunt
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import forge.game.ability.AbilityUtils;
|
||||||
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.trigger.Trigger;
|
||||||
|
import forge.game.trigger.TriggerHandler;
|
||||||
|
import forge.game.trigger.TriggerType;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ImmediateTriggerEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.card.abilityfactory.SpellEffect#resolve(java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
|
if (sa.hasParam("TriggerDescription")) {
|
||||||
|
return sa.getParam("TriggerDescription");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolve(SpellAbility sa) {
|
||||||
|
Map<String, String> mapParams = Maps.newHashMap(sa.getMapParams());
|
||||||
|
|
||||||
|
if (mapParams.containsKey("Cost")) {
|
||||||
|
mapParams.remove("Cost");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapParams.containsKey("SpellDescription")) {
|
||||||
|
mapParams.put("TriggerDescription", mapParams.get("SpellDescription"));
|
||||||
|
mapParams.remove("SpellDescription");
|
||||||
|
}
|
||||||
|
|
||||||
|
String triggerRemembered = null;
|
||||||
|
|
||||||
|
// Set Remembered
|
||||||
|
if (sa.hasParam("RememberObjects")) {
|
||||||
|
triggerRemembered = sa.getParam("RememberObjects");
|
||||||
|
}
|
||||||
|
|
||||||
|
mapParams.put("Mode", TriggerType.Immediate.name());
|
||||||
|
|
||||||
|
final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, sa.getHostCard(), true);
|
||||||
|
|
||||||
|
if (sa.hasParam("CopyTriggeringObjects")) {
|
||||||
|
immediateTrig.setStoredTriggeredObjects(sa.getTriggeringObjects());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to copy paid costs
|
||||||
|
|
||||||
|
if (triggerRemembered != null) {
|
||||||
|
for (final String rem : triggerRemembered.split(",")) {
|
||||||
|
for (final Object o : AbilityUtils.getDefinedObjects(sa.getHostCard(), rem, sa)) {
|
||||||
|
if (o instanceof SpellAbility) {
|
||||||
|
// "RememberObjects$ Remembered" don't remember spellability
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
immediateTrig.addRemembered(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapParams.containsKey("Execute") || sa.hasAdditionalAbility("Execute")) {
|
||||||
|
SpellAbility overridingSA = sa.getAdditionalAbility("Execute");
|
||||||
|
overridingSA.setActivatingPlayer(sa.getActivatingPlayer());
|
||||||
|
immediateTrig.setOverridingAbility(overridingSA);
|
||||||
|
}
|
||||||
|
final TriggerHandler trigHandler = sa.getActivatingPlayer().getGame().getTriggerHandler();
|
||||||
|
|
||||||
|
// Instead of registering this, add to the delayed triggers as an immediate trigger type? Which means it'll fire as soon as possible
|
||||||
|
trigHandler.registerDelayedTrigger(immediateTrig);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -104,7 +104,7 @@ public class MillEffect extends SpellAbilityEffect {
|
|||||||
sb.append("s");
|
sb.append("s");
|
||||||
}
|
}
|
||||||
final String millPosition = sa.hasParam("FromBottom") ? "bottom" : "top";
|
final String millPosition = sa.hasParam("FromBottom") ? "bottom" : "top";
|
||||||
sb.append(" from the " + millPosition + " of his or her library.");
|
sb.append(" from the " + millPosition + " of their library.");
|
||||||
|
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class MustAttackEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
for (final Player player : tgtPlayers) {
|
for (final Player player : tgtPlayers) {
|
||||||
sb.append("Creatures ").append(player).append(" controls attack ");
|
sb.append("Creatures ").append(player).append(" controls attack ");
|
||||||
sb.append(defender).append(" during his or her next turn.");
|
sb.append(defender).append(" during their next turn.");
|
||||||
}
|
}
|
||||||
for (final Card c : getTargetCards(sa)) {
|
for (final Card c : getTargetCards(sa)) {
|
||||||
sb.append(c).append(" must attack ");
|
sb.append(c).append(" must attack ");
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class ProtectAllEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
for (final Card tgtC : list) {
|
for (final Card tgtC : list) {
|
||||||
if (tgtC.isInPlay()) {
|
if (tgtC.isInPlay()) {
|
||||||
tgtC.addChangedCardKeywords(gainsKWList, ImmutableList.<String>of(), false, timestamp, true);
|
tgtC.addChangedCardKeywords(gainsKWList, null, false, false, timestamp, true);
|
||||||
|
|
||||||
if (!sa.hasParam("Permanent")) {
|
if (!sa.hasParam("Permanent")) {
|
||||||
// If not Permanent, remove protection at EOT
|
// If not Permanent, remove protection at EOT
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.GameCommand;
|
import forge.GameCommand;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
@@ -153,7 +152,7 @@ public class ProtectEffect extends SpellAbilityEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
tgtC.addChangedCardKeywords(gainsKWList, ImmutableList.<String>of(), false, timestamp, true);
|
tgtC.addChangedCardKeywords(gainsKWList, null, false, false, timestamp, true);
|
||||||
|
|
||||||
if (!sa.hasParam("Permanent")) {
|
if (!sa.hasParam("Permanent")) {
|
||||||
// If not Permanent, remove protection at EOT
|
// If not Permanent, remove protection at EOT
|
||||||
@@ -181,7 +180,7 @@ public class ProtectEffect extends SpellAbilityEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
unTgtC.addChangedCardKeywords(gainsKWList, ImmutableList.<String>of(), false, timestamp, true);
|
unTgtC.addChangedCardKeywords(gainsKWList, null, false, false, timestamp, true);
|
||||||
|
|
||||||
if (!sa.hasParam("Permanent")) {
|
if (!sa.hasParam("Permanent")) {
|
||||||
// If not Permanent, remove protection at EOT
|
// If not Permanent, remove protection at EOT
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ import forge.game.spellability.SpellAbility;
|
|||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
public class PumpAllEffect extends SpellAbilityEffect {
|
public class PumpAllEffect extends SpellAbilityEffect {
|
||||||
private static void applyPumpAll(final SpellAbility sa,
|
private static void applyPumpAll(final SpellAbility sa,
|
||||||
final List<Card> list, final int a, final int d,
|
final List<Card> list, final int a, final int d,
|
||||||
@@ -24,23 +25,18 @@ public class PumpAllEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
final Game game = sa.getActivatingPlayer().getGame();
|
||||||
final long timestamp = game.getNextTimestamp();
|
final long timestamp = game.getNextTimestamp();
|
||||||
final List<String> kws = new ArrayList<String>();
|
final List<String> kws = Lists.newArrayList();
|
||||||
final List<String> hiddenkws = new ArrayList<String>();
|
final List<String> hiddenkws = Lists.newArrayList();
|
||||||
boolean suspend = false;
|
|
||||||
|
|
||||||
for (String kw : keywords) {
|
for (String kw : keywords) {
|
||||||
if (kw.startsWith("HIDDEN")) {
|
if (kw.startsWith("HIDDEN")) {
|
||||||
hiddenkws.add(kw);
|
hiddenkws.add(kw);
|
||||||
} else {
|
} else {
|
||||||
kws.add(kw);
|
kws.add(kw);
|
||||||
if (kw.equals("Suspend")) {
|
|
||||||
suspend = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Card tgtC : list) {
|
for (final Card tgtC : list) {
|
||||||
|
|
||||||
// only pump things in the affected zones.
|
// only pump things in the affected zones.
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (final ZoneType z : affectedZones) {
|
for (final ZoneType z : affectedZones) {
|
||||||
@@ -55,7 +51,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
tgtC.addTempPowerBoost(a);
|
tgtC.addTempPowerBoost(a);
|
||||||
tgtC.addTempToughnessBoost(d);
|
tgtC.addTempToughnessBoost(d);
|
||||||
tgtC.addChangedCardKeywords(kws, new ArrayList<String>(), false, timestamp);
|
tgtC.addChangedCardKeywords(kws, null, false, false, timestamp);
|
||||||
|
|
||||||
for (String kw : hiddenkws) {
|
for (String kw : hiddenkws) {
|
||||||
tgtC.addHiddenExtrinsicKeyword(kw);
|
tgtC.addHiddenExtrinsicKeyword(kw);
|
||||||
@@ -118,13 +114,11 @@ public class PumpAllEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void resolve(final SpellAbility sa) {
|
public void resolve(final SpellAbility sa) {
|
||||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||||
final List<ZoneType> affectedZones = new ArrayList<ZoneType>();
|
final List<ZoneType> affectedZones = Lists.newArrayList();
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
final Game game = sa.getActivatingPlayer().getGame();
|
||||||
|
|
||||||
if (sa.hasParam("PumpZone")) {
|
if (sa.hasParam("PumpZone")) {
|
||||||
for (final String zone : sa.getParam("PumpZone").split(",")) {
|
affectedZones.addAll(ZoneType.listValueOf(sa.getParam("PumpZone")));
|
||||||
affectedZones.add(ZoneType.valueOf(zone));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
affectedZones.add(ZoneType.Battlefield);
|
affectedZones.add(ZoneType.Battlefield);
|
||||||
}
|
}
|
||||||
@@ -149,7 +143,10 @@ public class PumpAllEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
list = (CardCollection)AbilityUtils.filterListByType(list, valid, sa);
|
list = (CardCollection)AbilityUtils.filterListByType(list, valid, sa);
|
||||||
|
|
||||||
List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<String>();
|
List<String> keywords = Lists.newArrayList();
|
||||||
|
if (sa.hasParam("KW")) {
|
||||||
|
keywords.addAll(Arrays.asList(sa.getParam("KW").split(" & ")));
|
||||||
|
}
|
||||||
final int a = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumAtt"), sa, true);
|
final int a = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumAtt"), sa, true);
|
||||||
final int d = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumDef"), sa, true);
|
final int d = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumDef"), sa, true);
|
||||||
|
|
||||||
@@ -161,6 +158,8 @@ public class PumpAllEffect extends SpellAbilityEffect {
|
|||||||
keywords = CardFactoryUtil.sharedKeywords(keywords, restrictions, zones, sa.getHostCard());
|
keywords = CardFactoryUtil.sharedKeywords(keywords, restrictions, zones, sa.getHostCard());
|
||||||
}
|
}
|
||||||
applyPumpAll(sa, list, a, d, keywords, affectedZones);
|
applyPumpAll(sa, list, a, d, keywords, affectedZones);
|
||||||
|
|
||||||
|
replaceDying(sa);
|
||||||
} // pumpAllResolve()
|
} // pumpAllResolve()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import forge.game.event.GameEventCardStatsChanged;
|
|||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
import forge.util.Lang;
|
import forge.util.Lang;
|
||||||
@@ -31,32 +30,40 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
final int a, final int d, final List<String> keywords,
|
final int a, final int d, final List<String> keywords,
|
||||||
final long timestamp) {
|
final long timestamp) {
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
|
final Game game = host.getGame();
|
||||||
//if host is not on the battlefield don't apply
|
//if host is not on the battlefield don't apply
|
||||||
// Suspend should does Affect the Stack
|
// Suspend should does Affect the Stack
|
||||||
if (sa.hasParam("UntilLoseControlOfHost")
|
if (sa.hasParam("UntilLoseControlOfHost")
|
||||||
&& !(host.isInPlay() || host.isInZone(ZoneType.Stack))) {
|
&& !(host.isInPlay() || host.isInZone(ZoneType.Stack))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
|
||||||
|
// do Game Check there in case of LKI
|
||||||
|
final Card gameCard = game.getCardState(applyTo, null);
|
||||||
|
if (gameCard == null || !applyTo.equalsWithTimestamp(gameCard)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final List<String> kws = Lists.newArrayList();
|
final List<String> kws = Lists.newArrayList();
|
||||||
|
|
||||||
boolean redrawPT = false;
|
boolean redrawPT = false;
|
||||||
for (String kw : keywords) {
|
for (String kw : keywords) {
|
||||||
if (kw.startsWith("HIDDEN")) {
|
if (kw.startsWith("HIDDEN")) {
|
||||||
applyTo.addHiddenExtrinsicKeyword(kw);
|
gameCard.addHiddenExtrinsicKeyword(kw);
|
||||||
redrawPT |= kw.contains("CARDNAME's power and toughness are switched");
|
redrawPT |= kw.contains("CARDNAME's power and toughness are switched");
|
||||||
} else {
|
} else {
|
||||||
kws.add(kw);
|
kws.add(kw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyTo.addTempPowerBoost(a);
|
gameCard.addTempPowerBoost(a);
|
||||||
applyTo.addTempToughnessBoost(d);
|
gameCard.addTempToughnessBoost(d);
|
||||||
applyTo.addChangedCardKeywords(kws, Lists.<String>newArrayList(), false, timestamp);
|
gameCard.addChangedCardKeywords(kws, Lists.<String>newArrayList(), false, false, timestamp);
|
||||||
if (redrawPT) { applyTo.updatePowerToughnessForView(); }
|
if (redrawPT) {
|
||||||
|
gameCard.updatePowerToughnessForView();
|
||||||
|
}
|
||||||
|
|
||||||
if (sa.hasParam("LeaveBattlefield")) {
|
if (sa.hasParam("LeaveBattlefield")) {
|
||||||
addLeaveBattlefieldReplacement(applyTo, sa, sa.getParam("LeaveBattlefield"));
|
addLeaveBattlefieldReplacement(gameCard, sa, sa.getParam("LeaveBattlefield"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sa.hasParam("Permanent")) {
|
if (!sa.hasParam("Permanent")) {
|
||||||
@@ -66,8 +73,8 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
applyTo.addTempPowerBoost(-1 * a);
|
gameCard.addTempPowerBoost(-1 * a);
|
||||||
applyTo.addTempToughnessBoost(-1 * d);
|
gameCard.addTempToughnessBoost(-1 * d);
|
||||||
|
|
||||||
if (keywords.size() > 0) {
|
if (keywords.size() > 0) {
|
||||||
boolean redrawPT = false;
|
boolean redrawPT = false;
|
||||||
@@ -75,16 +82,16 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
for (String kw : keywords) {
|
for (String kw : keywords) {
|
||||||
redrawPT |= kw.contains("CARDNAME's power and toughness are switched");
|
redrawPT |= kw.contains("CARDNAME's power and toughness are switched");
|
||||||
if (kw.startsWith("HIDDEN")) {
|
if (kw.startsWith("HIDDEN")) {
|
||||||
applyTo.removeHiddenExtrinsicKeyword(kw);
|
gameCard.removeHiddenExtrinsicKeyword(kw);
|
||||||
if (redrawPT) {
|
if (redrawPT) {
|
||||||
applyTo.updatePowerToughnessForView();
|
gameCard.updatePowerToughnessForView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
applyTo.removeChangedCardKeywords(timestamp);
|
gameCard.removeChangedCardKeywords(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
game.fireEvent(new GameEventCardStatsChanged(applyTo));
|
game.fireEvent(new GameEventCardStatsChanged(gameCard));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (sa.hasParam("UntilEndOfCombat")) {
|
if (sa.hasParam("UntilEndOfCombat")) {
|
||||||
@@ -107,12 +114,19 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
game.getEndOfTurn().addUntil(untilEOT);
|
game.getEndOfTurn().addUntil(untilEOT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.fireEvent(new GameEventCardStatsChanged(applyTo));
|
game.fireEvent(new GameEventCardStatsChanged(gameCard));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyPump(final SpellAbility sa, final Player p,
|
private static void applyPump(final SpellAbility sa, final Player p,
|
||||||
final List<String> keywords, final long timestamp) {
|
final List<String> keywords, final long timestamp) {
|
||||||
final Game game = p.getGame();
|
final Game game = p.getGame();
|
||||||
|
final Card host = sa.getHostCard();
|
||||||
|
//if host is not on the battlefield don't apply
|
||||||
|
// Suspend should does Affect the Stack
|
||||||
|
if (sa.hasParam("UntilLoseControlOfHost")
|
||||||
|
&& !(host.isInPlay() || host.isInZone(ZoneType.Stack))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
p.addChangedKeywords(keywords, ImmutableList.<String>of(), timestamp);
|
p.addChangedKeywords(keywords, ImmutableList.<String>of(), timestamp);
|
||||||
|
|
||||||
if (!sa.hasParam("Permanent")) {
|
if (!sa.hasParam("Permanent")) {
|
||||||
@@ -134,6 +148,9 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
game.getEndOfCombat().addUntil(untilEOT);
|
game.getEndOfCombat().addUntil(untilEOT);
|
||||||
} else if (sa.hasParam("UntilYourNextUpkeep")) {
|
} else if (sa.hasParam("UntilYourNextUpkeep")) {
|
||||||
game.getUpkeep().addUntil(sa.getActivatingPlayer(), untilEOT);
|
game.getUpkeep().addUntil(sa.getActivatingPlayer(), untilEOT);
|
||||||
|
} else if (sa.hasParam("UntilLoseControlOfHost")) {
|
||||||
|
sa.getHostCard().addLeavesPlayCommand(untilEOT);
|
||||||
|
sa.getHostCard().addChangeControllerCommand(untilEOT);
|
||||||
} else {
|
} else {
|
||||||
game.getEndOfTurn().addUntil(untilEOT);
|
game.getEndOfTurn().addUntil(untilEOT);
|
||||||
}
|
}
|
||||||
@@ -208,7 +225,6 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
public void resolve(final SpellAbility sa) {
|
public void resolve(final SpellAbility sa) {
|
||||||
|
|
||||||
final List<Card> untargetedCards = Lists.newArrayList();
|
final List<Card> untargetedCards = Lists.newArrayList();
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
final Game game = sa.getActivatingPlayer().getGame();
|
||||||
final Card host = sa.getHostCard();
|
final Card host = sa.getHostCard();
|
||||||
final long timestamp = game.getNextTimestamp();
|
final long timestamp = game.getNextTimestamp();
|
||||||
@@ -251,7 +267,7 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
final String landtype = sa.getParam("DefinedLandwalk");
|
final String landtype = sa.getParam("DefinedLandwalk");
|
||||||
final Card c = AbilityUtils.getDefinedCards(host, landtype, sa).get(0);
|
final Card c = AbilityUtils.getDefinedCards(host, landtype, sa).get(0);
|
||||||
for (String type : c.getType()) {
|
for (String type : c.getType()) {
|
||||||
if (CardType.isALandType(type) || CardType.isABasicLandType(type)) {
|
if (CardType.isALandType(type)) {
|
||||||
keywords.add(type + "walk");
|
keywords.add(type + "walk");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,7 +356,7 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if pump is a target, make sure we can still target now
|
// if pump is a target, make sure we can still target now
|
||||||
if ((tgt != null) && !tgtC.canBeTargetedBy(sa)) {
|
if (sa.usesTargeting() && !tgtC.canBeTargetedBy(sa)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,5 +383,7 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
applyPump(sa, p, keywords, timestamp);
|
applyPump(sa, p, keywords, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replaceDying(sa);
|
||||||
} // pumpResolve()
|
} // pumpResolve()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class RearrangeTopOfLibraryEffect extends SpellAbilityEffect {
|
|||||||
ret.append("that");
|
ret.append("that");
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.append(" player shuffle his or her library.");
|
ret.append(" player shuffle their library.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret.toString();
|
return ret.toString();
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ public class ReplaceEffect extends SpellAbilityEffect {
|
|||||||
params.put(varName, AbilityUtils.calculateAmount(card, varValue, sa));
|
params.put(varName, AbilityUtils.calculateAmount(card, varValue, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.containsKey("EffectOnly")) {
|
||||||
|
params.put("EffectOnly", true);
|
||||||
|
}
|
||||||
|
|
||||||
//try to call replacementHandler with new Params
|
//try to call replacementHandler with new Params
|
||||||
ReplacementResult result = game.getReplacementHandler().run(params);
|
ReplacementResult result = game.getReplacementHandler().run(params);
|
||||||
switch (result) {
|
switch (result) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user