mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Merge branch 'Card-Forge:master' into charming
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.50-SNAPSHOT</version>
|
<version>1.6.54-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@@ -119,10 +119,26 @@
|
|||||||
<opts>
|
<opts>
|
||||||
<opt>-Dfile.encoding=UTF-8</opt>
|
<opt>-Dfile.encoding=UTF-8</opt>
|
||||||
<opt>--add-opens java.base/java.lang=ALL-UNNAMED</opt>
|
<opt>--add-opens java.base/java.lang=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.base/java.math=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.base/jdk.internal.misc=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.base/java.nio=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens=java.base/sun.nio.ch=ALL-UNNAMED</opt>
|
||||||
<opt>--add-opens java.base/java.util=ALL-UNNAMED</opt>
|
<opt>--add-opens java.base/java.util=ALL-UNNAMED</opt>
|
||||||
<opt>--add-opens java.base/java.lang.reflect=ALL-UNNAMED</opt>
|
<opt>--add-opens java.base/java.lang.reflect=ALL-UNNAMED</opt>
|
||||||
<opt>--add-opens java.base/java.text=ALL-UNNAMED</opt>
|
<opt>--add-opens java.base/java.text=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.desktop/java.awt=ALL-UNNAMED</opt>
|
||||||
<opt>--add-opens java.desktop/java.awt.font=ALL-UNNAMED</opt>
|
<opt>--add-opens java.desktop/java.awt.font=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.desktop/java.awt.image=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.desktop/java.awt.color=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.desktop/sun.awt.image=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.desktop/javax.swing=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.desktop/javax.swing.border=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.desktop/javax.swing.event=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.desktop/sun.swing=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.desktop/java.beans=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.base/java.util.concurrent=ALL-UNNAMED</opt>
|
||||||
|
<opt>--add-opens java.base/java.net=ALL-UNNAMED</opt>
|
||||||
|
<opt>-Dio.netty.tryReflectionSetAccessible=true</opt>
|
||||||
</opts>
|
</opts>
|
||||||
</jre>
|
</jre>
|
||||||
<versionInfo>
|
<versionInfo>
|
||||||
@@ -274,7 +290,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<artifactId>forge-gui-mobile</artifactId>
|
<artifactId>forge-gui-mobile</artifactId>
|
||||||
<version>1.6.50-SNAPSHOT</version>
|
<version>1.6.54-SNAPSHOT</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ java -version 1>nul 2>nul || (
|
|||||||
for /f tokens^=2^ delims^=.-_^+^" %%j in ('java -fullversion 2^>^&1') do set "jver=%%j"
|
for /f tokens^=2^ delims^=.-_^+^" %%j in ('java -fullversion 2^>^&1') do set "jver=%%j"
|
||||||
|
|
||||||
if %jver% GEQ 17 (
|
if %jver% GEQ 17 (
|
||||||
java --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED -Xmx4096m -Dfile.encoding=UTF-8 -jar $project.build.finalName$
|
java --add-opens java.desktop/java.beans=ALL-UNNAMED --add-opens java.desktop/javax.swing.border=ALL-UNNAMED --add-opens java.desktop/javax.swing.event=ALL-UNNAMED --add-opens java.desktop/sun.swing=ALL-UNNAMED --add-opens java.desktop/java.awt.image=ALL-UNNAMED --add-opens java.desktop/java.awt.color=ALL-UNNAMED --add-opens java.desktop/sun.awt.image=ALL-UNNAMED --add-opens java.desktop/javax.swing=ALL-UNNAMED --add-opens java.desktop/java.awt=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true -Xmx4096m -Dfile.encoding=UTF-8 -jar $project.build.finalName$
|
||||||
popd
|
popd
|
||||||
exit /b 0
|
exit /b 0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.50-SNAPSHOT</version>
|
<version>1.6.54-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-ai</artifactId>
|
<artifactId>forge-ai</artifactId>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package forge.ai;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import forge.game.staticability.StaticAbilityAssignCombatDamageAsUnblocked;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
@@ -540,7 +541,7 @@ public class AiAttackController {
|
|||||||
|
|
||||||
for (Card attacker : categorizedAttackers) {
|
for (Card attacker : categorizedAttackers) {
|
||||||
if (!CombatUtil.canBeBlocked(attacker, accountedBlockers, null)
|
if (!CombatUtil.canBeBlocked(attacker, accountedBlockers, null)
|
||||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)) {
|
||||||
unblockedAttackers.add(attacker);
|
unblockedAttackers.add(attacker);
|
||||||
} else {
|
} else {
|
||||||
if (predictEvasion) {
|
if (predictEvasion) {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import forge.game.cost.Cost;
|
|||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.staticability.StaticAbilityAssignCombatDamageAsUnblocked;
|
||||||
import forge.game.staticability.StaticAbilityCantAttackBlock;
|
import forge.game.staticability.StaticAbilityCantAttackBlock;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
@@ -204,7 +205,7 @@ public class AiBlockController {
|
|||||||
}
|
}
|
||||||
blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
|
blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
|
||||||
// 2.Blockers that won't get destroyed
|
// 2.Blockers that won't get destroyed
|
||||||
} else if (!attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
} else if (!StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)
|
||||||
&& !ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
&& !ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
||||||
blocker = ComputerUtilCard.getWorstCreatureAI(safeBlockers);
|
blocker = ComputerUtilCard.getWorstCreatureAI(safeBlockers);
|
||||||
// check whether it's better to block a creature without trample to absorb more damage
|
// check whether it's better to block a creature without trample to absorb more damage
|
||||||
@@ -215,7 +216,7 @@ public class AiBlockController {
|
|||||||
|| other.hasKeyword(Keyword.TRAMPLE)
|
|| other.hasKeyword(Keyword.TRAMPLE)
|
||||||
|| ComputerUtilCombat.attackerHasThreateningAfflict(other, ai)
|
|| ComputerUtilCombat.attackerHasThreateningAfflict(other, ai)
|
||||||
|| ComputerUtilCombat.canDestroyBlocker(ai, blocker, other, combat, false)
|
|| ComputerUtilCombat.canDestroyBlocker(ai, blocker, other, combat, false)
|
||||||
|| other.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(other)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,7 +669,7 @@ public class AiBlockController {
|
|||||||
Card attacker = attackers.get(0);
|
Card attacker = attackers.get(0);
|
||||||
|
|
||||||
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > 1
|
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > 1
|
||||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)
|
||||||
|| ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
|| ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
||||||
attackers.remove(0);
|
attackers.remove(0);
|
||||||
makeChumpBlocks(combat, attackers);
|
makeChumpBlocks(combat, attackers);
|
||||||
@@ -689,7 +690,7 @@ public class AiBlockController {
|
|||||||
}
|
}
|
||||||
if (other.getNetCombatDamage() >= damageAbsorbed
|
if (other.getNetCombatDamage() >= damageAbsorbed
|
||||||
&& !other.hasKeyword(Keyword.TRAMPLE)
|
&& !other.hasKeyword(Keyword.TRAMPLE)
|
||||||
&& !other.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
&& !StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(other)
|
||||||
&& !ComputerUtilCombat.attackerHasThreateningAfflict(other, ai)
|
&& !ComputerUtilCombat.attackerHasThreateningAfflict(other, ai)
|
||||||
&& CombatUtil.canBlock(other, blocker, combat)) {
|
&& CombatUtil.canBlock(other, blocker, combat)) {
|
||||||
combat.addBlocker(other, blocker);
|
combat.addBlocker(other, blocker);
|
||||||
@@ -756,7 +757,7 @@ public class AiBlockController {
|
|||||||
|
|
||||||
for (final Card attacker : tramplingAttackers) {
|
for (final Card attacker : tramplingAttackers) {
|
||||||
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > combat.getBlockers(attacker).size()
|
if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > combat.getBlockers(attacker).size()
|
||||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)
|
||||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -945,6 +946,39 @@ public class AiBlockController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void makeRequiredBlocks(Combat combat) {
|
||||||
|
// assign blockers that have to block
|
||||||
|
final CardCollection chumpBlockers = CardLists.getKeyword(blockersLeft, "CARDNAME blocks each combat if able.");
|
||||||
|
// if an attacker with lure attacks - all that can block
|
||||||
|
for (final Card blocker : blockersLeft) {
|
||||||
|
if (CombatUtil.mustBlockAnAttacker(blocker, combat, null)) {
|
||||||
|
chumpBlockers.add(blocker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!chumpBlockers.isEmpty()) {
|
||||||
|
for (final Card attacker : attackers) {
|
||||||
|
List<Card> blockers = getPossibleBlockers(combat, attacker, chumpBlockers, false);
|
||||||
|
for (final Card blocker : blockers) {
|
||||||
|
if (CombatUtil.canBlock(attacker, blocker, combat) && blockersLeft.contains(blocker)
|
||||||
|
&& (CombatUtil.mustBlockAnAttacker(blocker, combat, null)
|
||||||
|
|| blocker.hasKeyword("CARDNAME blocks each combat if able."))) {
|
||||||
|
combat.addBlocker(attacker, blocker);
|
||||||
|
if (!blocker.getMustBlockCards().isEmpty()) {
|
||||||
|
int mustBlockAmt = blocker.getMustBlockCards().size();
|
||||||
|
final CardCollectionView blockedSoFar = combat.getAttackersBlockedBy(blocker);
|
||||||
|
boolean canBlockAnother = CombatUtil.canBlockMoreCreatures(blocker, blockedSoFar);
|
||||||
|
if (!canBlockAnother || mustBlockAmt == blockedSoFar.size()) {
|
||||||
|
blockersLeft.remove(blocker);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blockersLeft.remove(blocker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void clearBlockers(final Combat combat, final List<Card> possibleBlockers) {
|
private void clearBlockers(final Combat combat, final List<Card> possibleBlockers) {
|
||||||
for (final Card blocker : CardLists.filterControlledBy(combat.getAllBlockers(), ai)) {
|
for (final Card blocker : CardLists.filterControlledBy(combat.getAllBlockers(), ai)) {
|
||||||
// don't touch other player's blockers
|
// don't touch other player's blockers
|
||||||
@@ -1008,9 +1042,6 @@ public class AiBlockController {
|
|||||||
|
|
||||||
clearBlockers(combat, possibleBlockers);
|
clearBlockers(combat, possibleBlockers);
|
||||||
|
|
||||||
List<Card> blockers;
|
|
||||||
List<Card> chumpBlockers;
|
|
||||||
|
|
||||||
diff = (ai.getLife() * 2) - 5; // This is the minimal gain for an unnecessary trade
|
diff = (ai.getLife() * 2) - 5; // This is the minimal gain for an unnecessary trade
|
||||||
if (ai.getController().isAI() && diff > 0 && ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.PLAY_AGGRO)) {
|
if (ai.getController().isAI() && diff > 0 && ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.PLAY_AGGRO)) {
|
||||||
diff = 0;
|
diff = 0;
|
||||||
@@ -1106,37 +1137,9 @@ public class AiBlockController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// assign blockers that have to block
|
// block requirements
|
||||||
chumpBlockers = CardLists.getKeyword(blockersLeft, "CARDNAME blocks each combat if able.");
|
// TODO because this isn't done earlier, sometimes a good block will enforce a restriction that prevents another for the requirement
|
||||||
// if an attacker with lure attacks - all that can block
|
makeRequiredBlocks(combat);
|
||||||
for (final Card blocker : blockersLeft) {
|
|
||||||
if (CombatUtil.mustBlockAnAttacker(blocker, combat, null)) {
|
|
||||||
chumpBlockers.add(blocker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!chumpBlockers.isEmpty()) {
|
|
||||||
CardLists.shuffle(attackers);
|
|
||||||
for (final Card attacker : attackers) {
|
|
||||||
blockers = getPossibleBlockers(combat, attacker, chumpBlockers, false);
|
|
||||||
for (final Card blocker : blockers) {
|
|
||||||
if (CombatUtil.canBlock(attacker, blocker, combat) && blockersLeft.contains(blocker)
|
|
||||||
&& (CombatUtil.mustBlockAnAttacker(blocker, combat, null)
|
|
||||||
|| blocker.hasKeyword("CARDNAME blocks each combat if able."))) {
|
|
||||||
combat.addBlocker(attacker, blocker);
|
|
||||||
if (!blocker.getMustBlockCards().isEmpty()) {
|
|
||||||
int mustBlockAmt = blocker.getMustBlockCards().size();
|
|
||||||
final CardCollectionView blockedSoFar = combat.getAttackersBlockedBy(blocker);
|
|
||||||
boolean canBlockAnother = CombatUtil.canBlockMoreCreatures(blocker, blockedSoFar);
|
|
||||||
if (!canBlockAnother || mustBlockAmt == blockedSoFar.size()) {
|
|
||||||
blockersLeft.remove(blocker);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
blockersLeft.remove(blocker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to see if it's possible to defend a Planeswalker under attack with a chump block,
|
// check to see if it's possible to defend a Planeswalker under attack with a chump block,
|
||||||
// unless life is low enough to be more worried about saving preserving the life total
|
// unless life is low enough to be more worried about saving preserving the life total
|
||||||
@@ -1186,8 +1189,7 @@ public class AiBlockController {
|
|||||||
* Orders a blocker that put onto the battlefield blocking. Depends heavily
|
* Orders a blocker that put onto the battlefield blocking. Depends heavily
|
||||||
* on the implementation of orderBlockers().
|
* on the implementation of orderBlockers().
|
||||||
*/
|
*/
|
||||||
public static CardCollection orderBlocker(final Card attacker, final Card blocker,
|
public static CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) {
|
||||||
final CardCollection oldBlockers) {
|
|
||||||
// add blocker to existing ordering
|
// add blocker to existing ordering
|
||||||
// sort by evaluate, then insert it appropriately
|
// sort by evaluate, then insert it appropriately
|
||||||
// relies on current implementation of orderBlockers()
|
// relies on current implementation of orderBlockers()
|
||||||
|
|||||||
@@ -889,6 +889,10 @@ public class AiController {
|
|||||||
spellHost.setLastKnownZone(game.getStackZone()); // need to add to stack to make check Restrictions respect stack cmc
|
spellHost.setLastKnownZone(game.getStackZone()); // need to add to stack to make check Restrictions respect stack cmc
|
||||||
spellHost.setCastFrom(card.getZone());
|
spellHost.setCastFrom(card.getZone());
|
||||||
}
|
}
|
||||||
|
// TODO maybe other location for this?
|
||||||
|
if (!sa.isLegalAfterStack()) {
|
||||||
|
return AiPlayDecision.AnotherTime;
|
||||||
|
}
|
||||||
if (!sa.checkRestrictions(spellHost, player)) {
|
if (!sa.checkRestrictions(spellHost, player)) {
|
||||||
return AiPlayDecision.AnotherTime;
|
return AiPlayDecision.AnotherTime;
|
||||||
}
|
}
|
||||||
@@ -1322,7 +1326,7 @@ public class AiController {
|
|||||||
return discardList;
|
return discardList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
if (mode == PlayerActionConfirmMode.AlternativeDamageAssignment) {
|
if (mode == PlayerActionConfirmMode.AlternativeDamageAssignment) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1335,7 +1339,7 @@ public class AiController {
|
|||||||
mode);
|
mode);
|
||||||
throw new IllegalArgumentException(exMsg);
|
throw new IllegalArgumentException(exMsg);
|
||||||
}
|
}
|
||||||
return SpellApiToAi.Converter.get(api).confirmAction(player, sa, mode, message);
|
return SpellApiToAi.Converter.get(api).confirmAction(player, sa, mode, message, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode mode, String message, int bid, Player winner) {
|
public boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode mode, String message, int bid, Player winner) {
|
||||||
@@ -1925,8 +1929,8 @@ public class AiController {
|
|||||||
return Math.min(player.getLife() -1,MyRandom.getRandom().nextInt(Math.max(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1);
|
return Math.min(player.getLife() -1,MyRandom.getRandom().nextInt(Math.max(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1);
|
||||||
} else if ("HighestGetCounter".equals(logic)) {
|
} else if ("HighestGetCounter".equals(logic)) {
|
||||||
return MyRandom.getRandom().nextInt(3);
|
return MyRandom.getRandom().nextInt(3);
|
||||||
} else if (source.hasSVar("EnergyToPay")) {
|
} else if (sa.hasSVar("EnergyToPay")) {
|
||||||
return AbilityUtils.calculateAmount(source, source.getSVar("EnergyToPay"), sa);
|
return AbilityUtils.calculateAmount(source, sa.getSVar("EnergyToPay"), sa);
|
||||||
} else if ("Vermin".equals(logic)) {
|
} else if ("Vermin".equals(logic)) {
|
||||||
return MyRandom.getRandom().nextInt(Math.max(player.getLife() - 5, 0));
|
return MyRandom.getRandom().nextInt(Math.max(player.getLife() - 5, 0));
|
||||||
} else if ("SweepCreatures".equals(logic)) {
|
} else if ("SweepCreatures".equals(logic)) {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import forge.game.replacement.ReplacementLayer;
|
|||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
|
import forge.game.staticability.StaticAbilityAssignCombatDamageAsUnblocked;
|
||||||
import forge.game.staticability.StaticAbilityMustAttack;
|
import forge.game.staticability.StaticAbilityMustAttack;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
@@ -325,8 +326,7 @@ public class ComputerUtilCombat {
|
|||||||
final List<Card> blockers = combat.getBlockers(attacker);
|
final List<Card> blockers = combat.getBlockers(attacker);
|
||||||
|
|
||||||
if (blockers.size() == 0
|
if (blockers.size() == 0
|
||||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage "
|
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)) {
|
||||||
+ "as though it weren't blocked.")) {
|
|
||||||
unblocked.add(attacker);
|
unblocked.add(attacker);
|
||||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE)
|
} else if (attacker.hasKeyword(Keyword.TRAMPLE)
|
||||||
&& getAttack(attacker) > totalShieldDamage(attacker, blockers)) {
|
&& getAttack(attacker) > totalShieldDamage(attacker, blockers)) {
|
||||||
@@ -367,8 +367,7 @@ public class ComputerUtilCombat {
|
|||||||
final List<Card> blockers = combat.getBlockers(attacker);
|
final List<Card> blockers = combat.getBlockers(attacker);
|
||||||
|
|
||||||
if (blockers.size() == 0
|
if (blockers.size() == 0
|
||||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage"
|
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)) {
|
||||||
+ " as though it weren't blocked.")) {
|
|
||||||
unblocked.add(attacker);
|
unblocked.add(attacker);
|
||||||
} else if (attacker.hasKeyword(Keyword.TRAMPLE)
|
} else if (attacker.hasKeyword(Keyword.TRAMPLE)
|
||||||
&& getAttack(attacker) > totalShieldDamage(attacker, blockers)) {
|
&& getAttack(attacker) > totalShieldDamage(attacker, blockers)) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import forge.game.card.CounterEnumType;
|
|||||||
import forge.game.cost.CostPayEnergy;
|
import forge.game.cost.CostPayEnergy;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.staticability.StaticAbilityAssignCombatDamageAsUnblocked;
|
||||||
import forge.game.staticability.StaticAbilityMustAttack;
|
import forge.game.staticability.StaticAbilityMustAttack;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -62,7 +63,8 @@ public class CreatureEvaluator implements Function<Card, Integer> {
|
|||||||
if (c.hasKeyword("Unblockable")) {
|
if (c.hasKeyword("Unblockable")) {
|
||||||
value += addValue(power * 10, "unblockable");
|
value += addValue(power * 10, "unblockable");
|
||||||
} else {
|
} else {
|
||||||
if (c.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
if (StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(c)
|
||||||
|
|| StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(c, false)) {
|
||||||
value += addValue(power * 6, "thorns");
|
value += addValue(power * 6, "thorns");
|
||||||
}
|
}
|
||||||
if (c.hasKeyword(Keyword.FEAR)) {
|
if (c.hasKeyword(Keyword.FEAR)) {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import forge.card.MagicColor;
|
|||||||
import forge.card.mana.ManaAtom;
|
import forge.card.mana.ManaAtom;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.GameObject;
|
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.effects.DetachedCardEffect;
|
import forge.game.ability.effects.DetachedCardEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -207,7 +206,7 @@ public abstract class GameState {
|
|||||||
cardsReferencedByID.add(card.getExiledWith());
|
cardsReferencedByID.add(card.getExiledWith());
|
||||||
}
|
}
|
||||||
if (zone == ZoneType.Battlefield) {
|
if (zone == ZoneType.Battlefield) {
|
||||||
if (!card.getAttachedCards().isEmpty()) {
|
if (card.hasCardAttachments()) {
|
||||||
// Remember the ID of cards that have attachments
|
// Remember the ID of cards that have attachments
|
||||||
cardsReferencedByID.add(card);
|
cardsReferencedByID.add(card);
|
||||||
}
|
}
|
||||||
@@ -376,7 +375,7 @@ public abstract class GameState {
|
|||||||
newText.append("|Imprinting:").append(TextUtil.join(imprintedCardIds, ","));
|
newText.append("|Imprinting:").append(TextUtil.join(imprintedCardIds, ","));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c.getMergedCards().isEmpty()) {
|
if (c.hasMergedCard()) {
|
||||||
List<String> mergedCardNames = new ArrayList<>();
|
List<String> mergedCardNames = new ArrayList<>();
|
||||||
for (Card merged : c.getMergedCards()) {
|
for (Card merged : c.getMergedCards()) {
|
||||||
if (c.getTopMergedCard() == merged) {
|
if (c.getTopMergedCard() == merged) {
|
||||||
@@ -862,9 +861,7 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("RememberTargets")) {
|
if (sa.hasParam("RememberTargets")) {
|
||||||
for (final GameObject o : sa.getTargets()) {
|
sa.getHostCard().addRemembered(sa.getTargets());
|
||||||
sa.getHostCard().addRemembered(o);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -270,8 +270,8 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return getAi().confirmAction(sa, mode, message);
|
return getAi().confirmAction(sa, mode, message, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ public abstract class SpellAbilityAi {
|
|||||||
return SpellApiToAi.Converter.get(ab.getApi()).chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb));
|
return SpellApiToAi.Converter.get(ab.getApi()).chkAIDrawback(ab, aiPlayer) && (subAb == null || chkDrawbackWithSubs(aiPlayer, subAb));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of confirmAction 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 confirmAction is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ public enum SpellApiToAi {
|
|||||||
.put(ApiType.DigMultiple, DigMultipleAi.class)
|
.put(ApiType.DigMultiple, DigMultipleAi.class)
|
||||||
.put(ApiType.DigUntil, DigUntilAi.class)
|
.put(ApiType.DigUntil, DigUntilAi.class)
|
||||||
.put(ApiType.Discard, DiscardAi.class)
|
.put(ApiType.Discard, DiscardAi.class)
|
||||||
|
.put(ApiType.Draft, ChooseCardNameAi.class)
|
||||||
.put(ApiType.DrainMana, DrainManaAi.class)
|
.put(ApiType.DrainMana, DrainManaAi.class)
|
||||||
.put(ApiType.Draw, DrawAi.class)
|
.put(ApiType.Draw, DrawAi.class)
|
||||||
.put(ApiType.EachDamage, DamageEachAi.class)
|
.put(ApiType.EachDamage, DamageEachAi.class)
|
||||||
@@ -169,6 +170,7 @@ public enum SpellApiToAi {
|
|||||||
.put(ApiType.StoreSVar, StoreSVarAi.class)
|
.put(ApiType.StoreSVar, StoreSVarAi.class)
|
||||||
.put(ApiType.Subgame, AlwaysPlayAi.class)
|
.put(ApiType.Subgame, AlwaysPlayAi.class)
|
||||||
.put(ApiType.Surveil, SurveilAi.class)
|
.put(ApiType.Surveil, SurveilAi.class)
|
||||||
|
.put(ApiType.TakeInitiative, AlwaysPlayAi.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)
|
||||||
@@ -184,7 +186,6 @@ public enum SpellApiToAi {
|
|||||||
.put(ApiType.WinsGame, GameWinAi.class)
|
.put(ApiType.WinsGame, GameWinAi.class)
|
||||||
|
|
||||||
.put(ApiType.DamageResolve, AlwaysPlayAi.class)
|
.put(ApiType.DamageResolve, AlwaysPlayAi.class)
|
||||||
.put(ApiType.InternalEtbReplacement, CanPlayAsDrawbackAi.class)
|
|
||||||
.put(ApiType.InternalLegendaryRule, LegendaryRuleAi.class)
|
.put(ApiType.InternalLegendaryRule, LegendaryRuleAi.class)
|
||||||
.put(ApiType.InternalIgnoreEffect, CannotPlayAi.class)
|
.put(ApiType.InternalIgnoreEffect, CannotPlayAi.class)
|
||||||
.build());
|
.build());
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
@@ -16,7 +18,7 @@ public class AlwaysPlayAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public class AmassAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return player.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2);
|
return player.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,17 +500,19 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// give sVars
|
// give sVars
|
||||||
if (sVars.size() > 0) {
|
if (sa.hasParam("sVars")) {
|
||||||
for (final String s : sVars) {
|
Map<String, String> sVarsMap = Maps.newHashMap();
|
||||||
String actualsVar = source.getSVar(s);
|
for (final String s : sa.getParam("sVars").split(",")) {
|
||||||
|
String actualsVar = AbilityUtils.getSVar(sa, s);
|
||||||
String name = s;
|
String name = s;
|
||||||
if (actualsVar.startsWith("SVar:")) {
|
if (actualsVar.startsWith("SVar:")) {
|
||||||
actualsVar = actualsVar.split("SVar:")[1];
|
actualsVar = actualsVar.split("SVar:")[1];
|
||||||
name = actualsVar.split(":")[0];
|
name = actualsVar.split(":")[0];
|
||||||
actualsVar = actualsVar.split(":")[1];
|
actualsVar = actualsVar.split(":")[1];
|
||||||
}
|
}
|
||||||
card.setSVar(name, actualsVar);
|
sVarsMap.put(name, actualsVar);
|
||||||
}
|
}
|
||||||
|
card.addChangedSVars(sVarsMap, timestamp, 0);
|
||||||
}
|
}
|
||||||
ComputerUtilCard.applyStaticContPT(game, card, null);
|
ComputerUtilCard.applyStaticContPT(game, card, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1729,7 +1729,7 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package forge.ai.ability;
|
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
|
||||||
import forge.game.player.Player;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
|
||||||
|
|
||||||
public class CanPlayAsDrawbackAi extends SpellAbilityAi {
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* copySpellTriggerAI.
|
|
||||||
* </p>
|
|
||||||
* @param sa
|
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
|
||||||
* @param mandatory
|
|
||||||
* a boolean.
|
|
||||||
* @param af
|
|
||||||
* a {@link forge.game.ability.AbilityFactory} object.
|
|
||||||
*
|
|
||||||
* @return a boolean.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1739,7 +1739,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
// AI was never asked
|
// AI was never asked
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
@@ -334,7 +335,7 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final String hostName = source.getName();
|
final String hostName = source.getName();
|
||||||
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
final ZoneType origin = ZoneType.listValueOf(sa.getParam("Origin")).get(0);
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ public class CloneAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
if (sa.hasParam("AILogic") && (!sa.usesTargeting() || sa.isTargetNumberValid())) {
|
if (sa.hasParam("AILogic") && (!sa.usesTargeting() || sa.isTargetNumberValid())) {
|
||||||
// Had a special logic for it and managed to target, so confirm if viable
|
// Had a special logic for it and managed to target, so confirm if viable
|
||||||
if ("CloneBestCreature".equals(sa.getParam("AILogic"))) {
|
if ("CloneBestCreature".equals(sa.getParam("AILogic"))) {
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
//TODO: add logic here
|
//TODO: add logic here
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
// Chain of Acid requires special attention here since otherwise the AI will confirm the copy and then
|
// Chain of Acid requires special attention here since otherwise the AI will confirm the copy and then
|
||||||
// run into the necessity of confirming a mandatory Destroy, thus destroying all of its own permanents.
|
// run into the necessity of confirming a mandatory Destroy, thus destroying all of its own permanents.
|
||||||
if ("ChainOfAcid".equals(sa.getParam("AILogic"))) {
|
if ("ChainOfAcid".equals(sa.getParam("AILogic"))) {
|
||||||
|
|||||||
@@ -752,14 +752,16 @@ public class CountersPutAi extends CountersAi {
|
|||||||
final int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
final int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||||
int left = amount;
|
int left = amount;
|
||||||
final String[] types;
|
final String[] types;
|
||||||
|
String type = "";
|
||||||
if (sa.hasParam("CounterType")) {
|
if (sa.hasParam("CounterType")) {
|
||||||
// TODO some cards let you choose types, should check each
|
// TODO some cards let you choose types, should check each
|
||||||
types = sa.getParam("CounterType").split(",");
|
types = sa.getParam("CounterType").split(",");
|
||||||
} else {
|
type = types[0];
|
||||||
|
} else if (sa.hasParam("CounterTypes")) {
|
||||||
// all types will be added
|
// all types will be added
|
||||||
types = sa.getParam("CounterTypes").split(",");
|
types = sa.getParam("CounterTypes").split(",");
|
||||||
|
type = types[0];
|
||||||
}
|
}
|
||||||
final String type = types[0];
|
|
||||||
|
|
||||||
if (!sa.usesTargeting()) {
|
if (!sa.usesTargeting()) {
|
||||||
// No target. So must be defined
|
// No target. So must be defined
|
||||||
@@ -911,7 +913,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
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
|
||||||
|
|||||||
@@ -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.Lists;
|
import com.google.common.collect.Lists;
|
||||||
@@ -151,7 +152,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return player.getCreaturesInPlay().size() >= player.getWeakestOpponent().getCreaturesInPlay().size();
|
return player.getCreaturesInPlay().size() >= player.getWeakestOpponent().getCreaturesInPlay().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import forge.util.MyRandom;
|
|||||||
public class DamageDealAi extends DamageAiBase {
|
public class DamageDealAi extends DamageAiBase {
|
||||||
@Override
|
@Override
|
||||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||||
|
final SpellAbility root = sa.getRootAbility();
|
||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
|
||||||
@@ -76,7 +77,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (dmg > energy || dmg < 1) {
|
if (dmg > energy || dmg < 1) {
|
||||||
continue; // in case the calculation gets messed up somewhere
|
continue; // in case the calculation gets messed up somewhere
|
||||||
}
|
}
|
||||||
source.setSVar("EnergyToPay", "Number$" + dmg);
|
root.setSVar("EnergyToPay", "Number$" + dmg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1075,6 +1076,11 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// chaining to this could miscalculate
|
||||||
|
if (sa.isDividedAsYouChoose()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Try to chain damage/debuff effects
|
// Try to chain damage/debuff effects
|
||||||
if (StringUtils.isNumeric(damage) || (damage.startsWith("-") && StringUtils.isNumeric(damage.substring(1)))) {
|
if (StringUtils.isNumeric(damage) || (damage.startsWith("-") && StringUtils.isNumeric(damage.substring(1)))) {
|
||||||
// currently only works for predictable numeric damage
|
// currently only works for predictable numeric damage
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
@@ -30,7 +32,7 @@ public class DayTimeAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -353,9 +353,7 @@ public class DestroyAi extends SpellAbilityAi {
|
|||||||
// Filter AI-specific targets if provided
|
// Filter AI-specific targets if provided
|
||||||
preferred = ComputerUtil.filterAITgts(sa, ai, preferred, true);
|
preferred = ComputerUtil.filterAITgts(sa, ai, preferred, true);
|
||||||
|
|
||||||
for (final Card c : preferred) {
|
list.removeAll(preferred);
|
||||||
list.remove(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preferred.isEmpty() && !mandatory) {
|
if (preferred.isEmpty() && !mandatory) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ public class DigAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
Card topc = player.getZone(ZoneType.Library).get(0);
|
Card topc = player.getZone(ZoneType.Library).get(0);
|
||||||
|
|
||||||
// AI actions for individual cards (until this AI can be generalized)
|
// AI actions for individual cards (until this AI can be generalized)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.AiAttackController;
|
import forge.ai.AiAttackController;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
@@ -94,7 +96,7 @@ public class DigMultipleAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 forge.ai.AiAttackController;
|
import forge.ai.AiAttackController;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
@@ -122,7 +123,7 @@ public class DigUntilAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
if (sa.hasParam("AILogic")) {
|
if (sa.hasParam("AILogic")) {
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
if ("OathOfDruids".equals(logic)) {
|
if ("OathOfDruids".equals(logic)) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package forge.ai.ability;
|
|||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
import forge.ai.ComputerUtilAbility;
|
import forge.ai.ComputerUtilAbility;
|
||||||
@@ -211,11 +212,11 @@ public class DiscardAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
if (mode == PlayerActionConfirmMode.Random) {
|
if (mode == PlayerActionConfirmMode.Random) {
|
||||||
// TODO For now AI will always discard Random used currently with: Balduvian Horde and similar cards
|
// TODO For now AI will always discard Random used currently with: Balduvian Horde and similar cards
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.confirmAction(player, sa, mode, message);
|
return super.confirmAction(player, sa, mode, message, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.AiCostDecision;
|
import forge.ai.AiCostDecision;
|
||||||
import forge.ai.AiProps;
|
import forge.ai.AiProps;
|
||||||
import forge.ai.ComputerUtil;
|
import forge.ai.ComputerUtil;
|
||||||
@@ -541,7 +543,7 @@ public class DrawAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
int numCards = sa.hasParam("NumCards") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa) : 1;
|
int numCards = sa.hasParam("NumCards") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa) : 1;
|
||||||
// AI shouldn't mill itself
|
// AI shouldn't mill itself
|
||||||
if (numCards < player.getZone(ZoneType.Library).size())
|
if (numCards < player.getZone(ZoneType.Library).size())
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public final class EncodeAi extends SpellAbilityAi {
|
|||||||
* forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
// only try to encode if there is a creature it can be used on
|
// only try to encode if there is a creature it can be used on
|
||||||
return chooseCard(player, player.getCreaturesInPlay(), true) != null;
|
return chooseCard(player, player.getCreaturesInPlay(), true) != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -42,7 +44,7 @@ public class FlipOntoBattlefieldAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
|
// always add to stack, targeting happens after payment
|
||||||
|
if (mandatory) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
String logic = sa.getParamOrDefault("AILogic", "");
|
String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
SpellAbility trigsa = sa.getAdditionalAbility("Execute");
|
SpellAbility trigsa = sa.getAdditionalAbility("Execute");
|
||||||
@@ -45,11 +50,7 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
|
|||||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
if (!sa.hasParam("OptionalDecider")) {
|
return aic.doTrigger(trigsa, !"You".equals(sa.getParamOrDefault("OptionalDecider", "You")));
|
||||||
return aic.doTrigger(trigsa, true);
|
|
||||||
} else {
|
|
||||||
return aic.doTrigger(trigsa, !sa.getParam("OptionalDecider").equals("You"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
@@ -20,7 +22,7 @@ public class InvestigateAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.PlayerControllerAi;
|
import forge.ai.PlayerControllerAi;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
@@ -32,7 +34,7 @@ public class LearnAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class ManifestAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ public class MillAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
if ("TimmerianFiends".equals(sa.getParam("AILogic"))) {
|
if ("TimmerianFiends".equals(sa.getParam("AILogic"))) {
|
||||||
return SpecialCardAi.TimmerianFiends.consider(player, sa);
|
return SpecialCardAi.TimmerianFiends.consider(player, sa);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public class MutateAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.AiAttackController;
|
import forge.ai.AiAttackController;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.ai.SpellApiToAi;
|
import forge.ai.SpellApiToAi;
|
||||||
@@ -70,7 +72,7 @@ public class PeekAndRevealAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
AbilitySub subAb = sa.getSubAbility();
|
AbilitySub subAb = sa.getSubAbility();
|
||||||
return subAb != null && SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(player, subAb);
|
return subAb != null && SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(player, subAb);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class PumpAi extends PumpAiBase {
|
public class PumpAi extends PumpAiBase {
|
||||||
|
|
||||||
@@ -731,7 +732,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
if (minus > energy || minus < 1) {
|
if (minus > energy || minus < 1) {
|
||||||
continue; // in case the calculation gets messed up somewhere
|
continue; // in case the calculation gets messed up somewhere
|
||||||
}
|
}
|
||||||
source.setSVar("EnergyToPay", "Number$" + minus);
|
root.setSVar("EnergyToPay", "Number$" + minus);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -781,7 +782,7 @@ public class PumpAi extends PumpAiBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
//TODO Add logic here if necessary but I think the AI won't cast
|
//TODO Add logic here if necessary but I think the AI won't cast
|
||||||
//the spell in the first place if it would curse its own creature
|
//the spell in the first place if it would curse its own creature
|
||||||
//and the pump isn't mandatory
|
//and the pump isn't mandatory
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.AiCardMemory;
|
import forge.ai.AiCardMemory;
|
||||||
import forge.ai.AiController;
|
import forge.ai.AiController;
|
||||||
import forge.ai.AiProps;
|
import forge.ai.AiProps;
|
||||||
@@ -92,7 +94,7 @@ public class RearrangeTopOfLibraryAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
// Confirming this action means shuffling the library if asked.
|
// Confirming this action means shuffling the library if asked.
|
||||||
|
|
||||||
// First, let's check if we can play the top card of the library
|
// First, let's check if we can play the top card of the library
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
@@ -40,7 +42,7 @@ public class RepeatAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
//TODO add logic to have computer make better choice (ArsenalNut)
|
//TODO add logic to have computer make better choice (ArsenalNut)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -43,7 +45,7 @@ public class RollDiceAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 forge.ai.ComputerUtilCard;
|
import forge.ai.ComputerUtilCard;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
@@ -175,7 +176,7 @@ public class SacrificeAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
|
|
||||||
import forge.ai.ComputerUtilMana;
|
import forge.ai.ComputerUtilMana;
|
||||||
@@ -132,7 +134,7 @@ public class ScryAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -264,7 +265,7 @@ public class SetStateAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
// TODO: improve the AI for when it may want to transform something that's optional to transform
|
// TODO: improve the AI for when it may want to transform something that's optional to transform
|
||||||
return isSafeToTransformIntoLegendary(player, sa.getHostCard());
|
return isSafeToTransformIntoLegendary(player, sa.getHostCard());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
@@ -54,7 +56,7 @@ public class ShuffleAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
// ai could analyze parameter denoting the player to shuffle
|
// ai could analyze parameter denoting the player to shuffle
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.AiAttackController;
|
import forge.ai.AiAttackController;
|
||||||
import forge.ai.SpellAbilityAi;
|
import forge.ai.SpellAbilityAi;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
@@ -18,7 +20,7 @@ public class SkipPhaseAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.ai.AiCardMemory;
|
import forge.ai.AiCardMemory;
|
||||||
import forge.ai.AiProps;
|
import forge.ai.AiProps;
|
||||||
import forge.ai.ComputerUtilCost;
|
import forge.ai.ComputerUtilCost;
|
||||||
@@ -119,7 +121,7 @@ public class SurveilAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,10 +106,8 @@ public abstract class TapAiBase extends SpellAbilityAi {
|
|||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
protected boolean tapPrefTargeting(final Player ai, final Card source, final SpellAbility sa, final boolean mandatory) {
|
protected boolean tapPrefTargeting(final Player ai, final Card source, final SpellAbility sa, final boolean mandatory) {
|
||||||
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
|
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
CardCollection tapList = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
|
CardCollection tapList = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa);
|
||||||
tapList = CardLists.getTargetableCards(tapList, sa);
|
|
||||||
tapList = CardLists.filter(tapList, Presets.UNTAPPED);
|
tapList = CardLists.filter(tapList, Presets.UNTAPPED);
|
||||||
tapList = CardLists.filter(tapList, new Predicate<Card>() {
|
tapList = CardLists.filter(tapList, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -129,8 +127,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
|
|||||||
|
|
||||||
//use broader approach when the cost is a positive thing
|
//use broader approach when the cost is a positive thing
|
||||||
if (tapList.isEmpty() && ComputerUtil.activateForCost(sa, ai)) {
|
if (tapList.isEmpty() && ComputerUtil.activateForCost(sa, ai)) {
|
||||||
tapList = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
|
tapList = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa);
|
||||||
tapList = CardLists.getTargetableCards(tapList, sa);
|
|
||||||
tapList = CardLists.filter(tapList, new Predicate<Card>() {
|
tapList = CardLists.filter(tapList, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
@@ -150,8 +147,10 @@ public abstract class TapAiBase extends SpellAbilityAi {
|
|||||||
|
|
||||||
//try to exclude things that will already be tapped due to something on stack or because something is
|
//try to exclude things that will already be tapped due to something on stack or because something is
|
||||||
//already targeted in a parent or sub SA
|
//already targeted in a parent or sub SA
|
||||||
|
if (!sa.isTrigger() || mandatory) { // but if just confirming trigger no need to look for other targets and might still help anyway
|
||||||
CardCollection toExclude = ComputerUtilAbility.getCardsTargetedWithApi(ai, tapList, sa, ApiType.Tap);
|
CardCollection toExclude = ComputerUtilAbility.getCardsTargetedWithApi(ai, tapList, sa, ApiType.Tap);
|
||||||
tapList.removeAll(toExclude);
|
tapList.removeAll(toExclude);
|
||||||
|
}
|
||||||
|
|
||||||
if (tapList.isEmpty()) {
|
if (tapList.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -176,6 +175,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PhaseHandler phase = game.getPhaseHandler();
|
PhaseHandler phase = game.getPhaseHandler();
|
||||||
|
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
|
||||||
Card primeTarget = ComputerUtil.getKilledByTargeting(sa, tapList);
|
Card primeTarget = ComputerUtil.getKilledByTargeting(sa, tapList);
|
||||||
if (primeTarget != null) {
|
if (primeTarget != null) {
|
||||||
choice = primeTarget;
|
choice = primeTarget;
|
||||||
@@ -193,7 +193,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
|
|||||||
return CombatUtil.canAttack(c, opp);
|
return CombatUtil.canAttack(c, opp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
attackers.remove(sa.getHostCard());
|
attackers.remove(source);
|
||||||
}
|
}
|
||||||
Predicate<Card> findBlockers = CardPredicates.possibleBlockerForAtLeastOne(attackers);
|
Predicate<Card> findBlockers = CardPredicates.possibleBlockerForAtLeastOne(attackers);
|
||||||
List<Card> creatureList = CardLists.filter(tapList, findBlockers);
|
List<Card> creatureList = CardLists.filter(tapList, findBlockers);
|
||||||
@@ -202,7 +202,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
|
|||||||
|
|
||||||
if (!attackers.isEmpty() && !creatureList.isEmpty()) {
|
if (!attackers.isEmpty() && !creatureList.isEmpty()) {
|
||||||
choice = ComputerUtilCard.getBestCreatureAI(creatureList);
|
choice = ComputerUtilCard.getBestCreatureAI(creatureList);
|
||||||
} else if (sa.getRootAbility().isTrigger() || ComputerUtil.castSpellInMain1(ai, sa)) {
|
} else if (sa.isTrigger() || ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||||
choice = ComputerUtilCard.getMostExpensivePermanentAI(tapList);
|
choice = ComputerUtilCard.getMostExpensivePermanentAI(tapList);
|
||||||
}
|
}
|
||||||
} else if (phase.isPlayerTurn(opp)
|
} else if (phase.isPlayerTurn(opp)
|
||||||
@@ -272,7 +272,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter by enchantments and planeswalkers, their tapped state doesn't matter.
|
// filter by enchantments and planeswalkers, their tapped state (usually) doesn't matter.
|
||||||
final String[] tappablePermanents = { "Enchantment", "Planeswalker" };
|
final String[] tappablePermanents = { "Enchantment", "Planeswalker" };
|
||||||
tapList = CardLists.getValidCards(list, tappablePermanents, source.getController(), source, sa);
|
tapList = CardLists.getValidCards(list, tappablePermanents, source.getController(), source, sa);
|
||||||
|
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ public class TokenAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
// TODO: AILogic
|
// TODO: AILogic
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class UntapAi extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ComputerUtilCost.checkDiscardCost(ai, cost, sa.getHostCard(), sa);
|
return ComputerUtilCost.checkDiscardCost(ai, cost, source, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -174,8 +174,10 @@ public class UntapAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
//try to exclude things that will already be untapped due to something on stack or because something is
|
//try to exclude things that will already be untapped due to something on stack or because something is
|
||||||
//already targeted in a parent or sub SA
|
//already targeted in a parent or sub SA
|
||||||
|
if (!sa.isTrigger() || mandatory) { // but if just confirming trigger no need to look for other targets and might still help anyway
|
||||||
CardCollection toExclude = ComputerUtilAbility.getCardsTargetedWithApi(ai, untapList, sa, ApiType.Untap);
|
CardCollection toExclude = ComputerUtilAbility.getCardsTargetedWithApi(ai, untapList, sa, ApiType.Untap);
|
||||||
untapList.removeAll(toExclude);
|
untapList.removeAll(toExclude);
|
||||||
|
}
|
||||||
|
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
while (sa.canAddMoreTarget()) {
|
while (sa.canAddMoreTarget()) {
|
||||||
@@ -199,7 +201,7 @@ public class UntapAi extends SpellAbilityAi {
|
|||||||
if (choice == null) {
|
if (choice == null) {
|
||||||
if (CardLists.getNotType(untapList, "Creature").isEmpty()) {
|
if (CardLists.getNotType(untapList, "Creature").isEmpty()) {
|
||||||
choice = ComputerUtilCard.getBestCreatureAI(untapList); // if only creatures take the best
|
choice = ComputerUtilCard.getBestCreatureAI(untapList); // if only creatures take the best
|
||||||
} else if (!sa.getPayCosts().hasManaCost() || sa.getRootAbility().isTrigger()
|
} else if (!sa.getPayCosts().hasManaCost() || sa.isTrigger()
|
||||||
|| "Always".equals(sa.getParam("AILogic"))) {
|
|| "Always".equals(sa.getParam("AILogic"))) {
|
||||||
choice = ComputerUtilCard.getMostExpensivePermanentAI(untapList);
|
choice = ComputerUtilCard.getMostExpensivePermanentAI(untapList);
|
||||||
}
|
}
|
||||||
@@ -290,7 +292,7 @@ public class UntapAi extends SpellAbilityAi {
|
|||||||
choice = ComputerUtilCard.getBestAI(tapList);
|
choice = ComputerUtilCard.getBestAI(tapList);
|
||||||
|
|
||||||
if (choice == null) { // can't find anything left
|
if (choice == null) { // can't find anything left
|
||||||
if (sa.getTargets().size() < tgt.getMinTargets(sa.getHostCard(), sa) || sa.getTargets().size() == 0) {
|
if (sa.getTargets().size() < tgt.getMinTargets(source, sa) || sa.getTargets().size() == 0) {
|
||||||
if (!mandatory) {
|
if (!mandatory) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
}
|
}
|
||||||
@@ -310,9 +312,7 @@ public class UntapAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> list, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> list, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||||
PlayerCollection pl = new PlayerCollection();
|
PlayerCollection pl = ai.getYourTeam();
|
||||||
pl.add(ai);
|
|
||||||
pl.addAll(ai.getAllies());
|
|
||||||
return ComputerUtilCard.getBestAI(CardLists.filterControlledBy(list, pl));
|
return ComputerUtilCard.getBestAI(CardLists.filterControlledBy(list, pl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class VentureAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ public class GameCopier {
|
|||||||
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
|
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
|
||||||
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
|
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
|
||||||
newPlayer.setLifeGainedThisTurn(origPlayer.getLifeGainedThisTurn());
|
newPlayer.setLifeGainedThisTurn(origPlayer.getLifeGainedThisTurn());
|
||||||
|
// TODO creatureAttackedThisTurn
|
||||||
for (Mana m : origPlayer.getManaPool()) {
|
for (Mana m : origPlayer.getManaPool()) {
|
||||||
newPlayer.getManaPool().addMana(m, false);
|
newPlayer.getManaPool().addMana(m, false);
|
||||||
}
|
}
|
||||||
@@ -207,6 +208,13 @@ public class GameCopier {
|
|||||||
|
|
||||||
private void copyGameState(Game newGame) {
|
private void copyGameState(Game newGame) {
|
||||||
newGame.setAge(origGame.getAge());
|
newGame.setAge(origGame.getAge());
|
||||||
|
|
||||||
|
// TODO countersAddedThisTurn
|
||||||
|
|
||||||
|
if (origGame.getMonarch() != null) {
|
||||||
|
newGame.setMonarch(playerMap.get(origGame.getMonarch()));
|
||||||
|
}
|
||||||
|
|
||||||
for (ZoneType zone : ZONES) {
|
for (ZoneType zone : ZONES) {
|
||||||
for (Card card : origGame.getCardsIn(zone)) {
|
for (Card card : origGame.getCardsIn(zone)) {
|
||||||
addCard(newGame, zone, card);
|
addCard(newGame, zone, card);
|
||||||
@@ -300,6 +308,7 @@ public class GameCopier {
|
|||||||
newCard.setPTCharacterDefiningTable(c.getSetPTCharacterDefiningTable());
|
newCard.setPTCharacterDefiningTable(c.getSetPTCharacterDefiningTable());
|
||||||
|
|
||||||
newCard.setPTBoost(c.getPTBoostTable());
|
newCard.setPTBoost(c.getPTBoostTable());
|
||||||
|
// TODO copy by map
|
||||||
newCard.setDamage(c.getDamage());
|
newCard.setDamage(c.getDamage());
|
||||||
|
|
||||||
newCard.setChangedCardColors(c.getChangedCardColorsTable());
|
newCard.setChangedCardColors(c.getChangedCardColorsTable());
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.50-SNAPSHOT</version>
|
<version>1.6.54-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-core</artifactId>
|
<artifactId>forge-core</artifactId>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package forge;
|
|||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.util.FileUtil;
|
import forge.util.FileUtil;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
import forge.util.ThreadUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -171,10 +172,18 @@ public final class ImageKeys {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
//setlookup
|
//setlookup
|
||||||
file = setLookUpFile(filename, fullborderFile);
|
if (hasSetLookup(filename)) {
|
||||||
if (file != null) {
|
//delay processing so gui is responsive
|
||||||
cachedCards.put(filename, file);
|
ThreadUtil.delay(60, new Runnable() {
|
||||||
return file;
|
@Override
|
||||||
|
public void run() {
|
||||||
|
File f = setLookUpFile(filename, fullborderFile);
|
||||||
|
if (f != null)
|
||||||
|
cachedCards.put(filename, f);
|
||||||
|
else //is null
|
||||||
|
missingCards.add(filename);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder
|
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder
|
||||||
@@ -250,7 +259,7 @@ public final class ImageKeys {
|
|||||||
|
|
||||||
// System.out.println("File not found, no image created: " + key);
|
// System.out.println("File not found, no image created: " + key);
|
||||||
//add missing cards - disable for desktop version for compatibility reasons with autodownloader
|
//add missing cards - disable for desktop version for compatibility reasons with autodownloader
|
||||||
if (isLibGDXPort)
|
if (isLibGDXPort && !hasSetLookup(filename)) //missing cards with setlookup is handled differently
|
||||||
missingCards.add(filename);
|
missingCards.add(filename);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -260,6 +269,13 @@ public final class ImageKeys {
|
|||||||
? StaticData.instance().getEditions().getCode2ByCode(edition) // by default 2-letter codes from MWS are used
|
? StaticData.instance().getEditions().getCode2ByCode(edition) // by default 2-letter codes from MWS are used
|
||||||
: CACHE_CARD_PICS_SUBDIR.get(edition); // may use custom paths though
|
: CACHE_CARD_PICS_SUBDIR.get(edition); // may use custom paths though
|
||||||
}
|
}
|
||||||
|
static boolean hasSetLookup(String filename) {
|
||||||
|
if (!StaticData.instance().getSetLookup().isEmpty()) {
|
||||||
|
return StaticData.instance().getSetLookup().keySet().stream().anyMatch(setKey -> filename.startsWith(setKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
private static File setLookUpFile(String filename, String fullborderFile) {
|
private static File setLookUpFile(String filename, String fullborderFile) {
|
||||||
if (!StaticData.instance().getSetLookup().isEmpty()) {
|
if (!StaticData.instance().getSetLookup().isEmpty()) {
|
||||||
for (String setKey : StaticData.instance().getSetLookup().keySet()) {
|
for (String setKey : StaticData.instance().getSetLookup().keySet()) {
|
||||||
|
|||||||
@@ -214,8 +214,15 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEnterableDungeon() {
|
||||||
|
if (mainPart.getOracleText().contains("You can't enter this dungeon unless")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return getType().isDungeon();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean canBeCommander() {
|
public boolean canBeCommander() {
|
||||||
if (mainPart.getOracleText().contains("can be your commander")) {
|
if (mainPart.getOracleText().contains("can be your commander") || canBeBackground()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
CardType type = mainPart.getType();
|
CardType type = mainPart.getType();
|
||||||
@@ -232,8 +239,15 @@ public final class CardRules implements ICardCharacteristics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean canBePartnerCommander() {
|
public boolean canBePartnerCommander() {
|
||||||
|
if (canBeBackground()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return canBeCommander() && (hasKeyword("Partner") || !this.partnerWith.isEmpty() ||
|
return canBeCommander() && (hasKeyword("Partner") || !this.partnerWith.isEmpty() ||
|
||||||
hasKeyword("Friends forever"));
|
hasKeyword("Friends forever") || hasKeyword("Choose a Background"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canBeBackground() {
|
||||||
|
return mainPart.getType().hasSubtype("Background");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canBeOathbreaker() {
|
public boolean canBeOathbreaker() {
|
||||||
|
|||||||
@@ -209,6 +209,16 @@ public final class CardRulesPredicates {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Predicate<CardRules> deckHasExactly(final DeckHints.Type type, final String has[]) {
|
||||||
|
return new Predicate<CardRules>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(final CardRules card) {
|
||||||
|
DeckHints deckHas = card.getAiHints().getDeckHas();
|
||||||
|
return deckHas != null && deckHas.isValid() && deckHas.is(type, has);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core type.
|
* Core type.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import com.google.common.base.Predicate;
|
|||||||
import com.google.common.collect.BiMap;
|
import com.google.common.collect.BiMap;
|
||||||
import com.google.common.collect.HashBiMap;
|
import com.google.common.collect.HashBiMap;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
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.Sets;
|
import com.google.common.collect.Sets;
|
||||||
@@ -50,6 +51,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
private static final long serialVersionUID = 4629853583167022151L;
|
private static final long serialVersionUID = 4629853583167022151L;
|
||||||
|
|
||||||
public static final CardTypeView EMPTY = new CardType(false);
|
public static final CardTypeView EMPTY = new CardType(false);
|
||||||
|
private static final Set<String> multiWordTypes = ImmutableSet.of("Serra's Realm", "Bolas's Meditation Realm", "Dungeon Master");
|
||||||
|
|
||||||
public enum CoreType {
|
public enum CoreType {
|
||||||
Artifact(true, "artifacts"),
|
Artifact(true, "artifacts"),
|
||||||
@@ -71,6 +73,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
public final String pluralName;
|
public final String pluralName;
|
||||||
private static Map<String, CoreType> stringToCoreType = EnumUtils.getEnumMap(CoreType.class);
|
private static Map<String, CoreType> stringToCoreType = EnumUtils.getEnumMap(CoreType.class);
|
||||||
private static final Set<String> allCoreTypeNames = stringToCoreType.keySet();
|
private static final Set<String> allCoreTypeNames = stringToCoreType.keySet();
|
||||||
|
public static final Set<CoreType> spellTypes = ImmutableSet.of(Instant, Sorcery);
|
||||||
|
|
||||||
public static CoreType getEnum(String name) {
|
public static CoreType getEnum(String name) {
|
||||||
return stringToCoreType.get(name);
|
return stringToCoreType.get(name);
|
||||||
@@ -535,7 +538,8 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
newType = new CardType(CardType.this);
|
newType = new CardType(CardType.this);
|
||||||
|
|
||||||
if (ct.isRemoveCardTypes()) {
|
if (ct.isRemoveCardTypes()) {
|
||||||
newType.coreTypes.clear();
|
// 205.1a However, an object with either the instant or sorcery card type retains that type.
|
||||||
|
newType.coreTypes.retainAll(CoreType.spellTypes);
|
||||||
}
|
}
|
||||||
if (ct.isRemoveSuperTypes()) {
|
if (ct.isRemoveSuperTypes()) {
|
||||||
newType.supertypes.clear();
|
newType.supertypes.clear();
|
||||||
@@ -612,6 +616,9 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
if (!isPlaneswalker()) {
|
if (!isPlaneswalker()) {
|
||||||
Iterables.removeIf(subtypes, Predicates.IS_WALKER_TYPE);
|
Iterables.removeIf(subtypes, Predicates.IS_WALKER_TYPE);
|
||||||
}
|
}
|
||||||
|
if (!isDungeon()) {
|
||||||
|
Iterables.removeIf(subtypes, Predicates.IS_DUNGEON_TYPE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -746,12 +753,14 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
while (hasMoreTypes) {
|
while (hasMoreTypes) {
|
||||||
final String type = typeText.substring(iTypeStart, iSpace == -1 ? typeText.length() : iSpace);
|
final String type = typeText.substring(iTypeStart, iSpace == -1 ? typeText.length() : iSpace);
|
||||||
hasMoreTypes = iSpace != -1;
|
hasMoreTypes = iSpace != -1;
|
||||||
if (!isMultiwordType(type) || !hasMoreTypes) {
|
final String rest = typeText.substring(iTypeStart);
|
||||||
|
if (isMultiwordType(rest)) {
|
||||||
|
result.add(rest);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
iTypeStart = iSpace + 1;
|
iTypeStart = iSpace + 1;
|
||||||
if (!"-".equals(type)) {
|
|
||||||
result.add(type);
|
result.add(type);
|
||||||
}
|
|
||||||
}
|
|
||||||
iSpace = typeText.indexOf(space, iSpace + 1);
|
iSpace = typeText.indexOf(space, iSpace + 1);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -769,13 +778,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isMultiwordType(final String type) {
|
private static boolean isMultiwordType(final String type) {
|
||||||
final String[] multiWordTypes = { "Serra's Realm", "Bolas's Meditation Realm", "Dungeon Master" };
|
return multiWordTypes.contains(type);
|
||||||
for (int i = 0; i < multiWordTypes.length; ++i) {
|
|
||||||
if (multiWordTypes[i].startsWith(type) && !multiWordTypes[i].equals(type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Constant {
|
public static class Constant {
|
||||||
@@ -787,6 +790,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
public static final Set<String> ENCHANTMENT_TYPES = Sets.newHashSet();
|
public static final Set<String> ENCHANTMENT_TYPES = Sets.newHashSet();
|
||||||
public static final Set<String> ARTIFACT_TYPES = Sets.newHashSet();
|
public static final Set<String> ARTIFACT_TYPES = Sets.newHashSet();
|
||||||
public static final Set<String> WALKER_TYPES = Sets.newHashSet();
|
public static final Set<String> WALKER_TYPES = Sets.newHashSet();
|
||||||
|
public static final Set<String> DUNGEON_TYPES = Sets.newHashSet();
|
||||||
|
|
||||||
// singular -> plural
|
// singular -> plural
|
||||||
public static final BiMap<String,String> pluralTypes = HashBiMap.create();
|
public static final BiMap<String,String> pluralTypes = HashBiMap.create();
|
||||||
@@ -846,6 +850,12 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
return CardType.isAPlaneswalkerType(input);
|
return CardType.isAPlaneswalkerType(input);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
public static Predicate<String> IS_DUNGEON_TYPE = new Predicate<String>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(String input) {
|
||||||
|
return CardType.isADungeonType(input);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
///////// Utility methods
|
///////// Utility methods
|
||||||
@@ -878,6 +888,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
sortedSubTypes.addAll(Constant.ENCHANTMENT_TYPES);
|
sortedSubTypes.addAll(Constant.ENCHANTMENT_TYPES);
|
||||||
sortedSubTypes.addAll(Constant.ARTIFACT_TYPES);
|
sortedSubTypes.addAll(Constant.ARTIFACT_TYPES);
|
||||||
sortedSubTypes.addAll(Constant.WALKER_TYPES);
|
sortedSubTypes.addAll(Constant.WALKER_TYPES);
|
||||||
|
sortedSubTypes.addAll(Constant.DUNGEON_TYPES);
|
||||||
Collections.sort(sortedSubTypes);
|
Collections.sort(sortedSubTypes);
|
||||||
}
|
}
|
||||||
return sortedSubTypes;
|
return sortedSubTypes;
|
||||||
@@ -933,6 +944,9 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
|||||||
return (Constant.SPELL_TYPES.contains(cardType));
|
return (Constant.SPELL_TYPES.contains(cardType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isADungeonType(final String cardType) {
|
||||||
|
return (Constant.DUNGEON_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.
|
||||||
* Otherwise, simply return the input.
|
* Otherwise, simply return the input.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.google.common.collect.Iterables;
|
|||||||
|
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.util.PredicateString.StringOp;
|
import forge.util.PredicateString.StringOp;
|
||||||
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeckHints provides the ability for a Card to "want" another Card or type of
|
* DeckHints provides the ability for a Card to "want" another Card or type of
|
||||||
@@ -73,12 +74,26 @@ public class DeckHints {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (Pair<Type, String> filter : filters) {
|
for (Pair<Type, String> filter : filters) {
|
||||||
if (filter.getLeft() == type && filter.getRight().equals(hint)) {
|
if (filter.getLeft() == type && filter.getRight().contains(hint)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public boolean is(Type type, String hints[]) {
|
||||||
|
if (filters == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (String hint : hints) {
|
||||||
|
for (Pair<Type, String> filter : filters) {
|
||||||
|
if (filter.getLeft() == type && filter.getRight().equals(hint)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Map of Cards by Type from the given Iterable<PaperCard> that match this
|
* Returns a Map of Cards by Type from the given Iterable<PaperCard> that match this
|
||||||
@@ -95,6 +110,10 @@ public class DeckHints {
|
|||||||
String param = pair.getRight();
|
String param = pair.getRight();
|
||||||
Iterable<PaperCard> cards = getCardsForFilter(cardList, type, param);
|
Iterable<PaperCard> cards = getCardsForFilter(cardList, type, param);
|
||||||
if (cards != null) {
|
if (cards != null) {
|
||||||
|
// if a type is used more than once intersect respective matches
|
||||||
|
if (ret.get(type) != null) {
|
||||||
|
Iterables.retainAll(cards, new FCollection<>(ret.get(type)));
|
||||||
|
}
|
||||||
ret.put(type, cards);
|
ret.put(type, cards);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,13 +162,16 @@ public class DeckHints {
|
|||||||
|
|
||||||
private Iterable<PaperCard> getCardsForFilter(Iterable<PaperCard> cardList, Type type, String param) {
|
private Iterable<PaperCard> getCardsForFilter(Iterable<PaperCard> cardList, Type type, String param) {
|
||||||
List<PaperCard> cards = new ArrayList<>();
|
List<PaperCard> cards = new ArrayList<>();
|
||||||
switch (type) {
|
|
||||||
case ABILITY:
|
// this is case ABILITY, but other types can also use this when the implicit parsing would miss
|
||||||
String[] abilities = param.split("\\|");
|
String[] abilities = param.split("\\|");
|
||||||
for (String ability : abilities) {
|
for (String ability : abilities) {
|
||||||
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHas(Type.ABILITY, ability), PaperCard.FN_GET_RULES));
|
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHas(type, ability), PaperCard.FN_GET_RULES));
|
||||||
}
|
}
|
||||||
break;
|
// bonus if a DeckHas can satisfy the type with multiple ones
|
||||||
|
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHasExactly(type, abilities), PaperCard.FN_GET_RULES));
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
case COLOR:
|
case COLOR:
|
||||||
String[] colors = param.split("\\|");
|
String[] colors = param.split("\\|");
|
||||||
for (String color : colors) {
|
for (String color : colors) {
|
||||||
@@ -187,6 +209,7 @@ public class DeckHints {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NONE:
|
case NONE:
|
||||||
|
case ABILITY: // already done above
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return cards;
|
return cards;
|
||||||
|
|||||||
@@ -262,6 +262,9 @@ public enum DeckFormat {
|
|||||||
} else if (a.getRules().hasKeyword("Friends forever") &&
|
} else if (a.getRules().hasKeyword("Friends forever") &&
|
||||||
b.getRules().hasKeyword("Friends forever")) {
|
b.getRules().hasKeyword("Friends forever")) {
|
||||||
// Stranger Things Secret Lair gimmick partner commander
|
// Stranger Things Secret Lair gimmick partner commander
|
||||||
|
} else if (a.getRules().hasKeyword("Choose a Background") && b.getRules().canBeBackground()
|
||||||
|
|| b.getRules().hasKeyword("Choose a Background") && a.getRules().canBeBackground()) {
|
||||||
|
// commander with background
|
||||||
} else {
|
} else {
|
||||||
return "has an illegal commander partnership";
|
return "has an illegal commander partnership";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,13 +76,19 @@ public class Localizer {
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public String getEnglishMessage(final String key, final Object... messageArguments) {
|
||||||
|
return getMessage(true, key, messageArguments);
|
||||||
|
}
|
||||||
//FIXME: localizer should return default value from english locale or it will crash some GUI element like the NewGameMenu->NewGameScreen Popup when returned null...
|
//FIXME: localizer should return default value from english locale or it will crash some GUI element like the NewGameMenu->NewGameScreen Popup when returned null...
|
||||||
public String getMessage(final String key, final Object... messageArguments) {
|
public String getMessage(final String key, final Object... messageArguments) {
|
||||||
|
return getMessage(false, key, messageArguments);
|
||||||
|
}
|
||||||
|
public String getMessage(final boolean forcedEnglish, final String key, final Object... messageArguments) {
|
||||||
MessageFormat formatter = null;
|
MessageFormat formatter = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//formatter = new MessageFormat(resourceBundle.getString(key.toLowerCase()), locale);
|
//formatter = new MessageFormat(resourceBundle.getString(key.toLowerCase()), locale);
|
||||||
formatter = new MessageFormat(english ? englishBundle.getString(key) : resourceBundle.getString(key), english ? Locale.ENGLISH : locale);
|
formatter = new MessageFormat(english || forcedEnglish ? englishBundle.getString(key) : resourceBundle.getString(key), english || forcedEnglish ? Locale.ENGLISH : locale);
|
||||||
} catch (final IllegalArgumentException | MissingResourceException e) {
|
} catch (final IllegalArgumentException | MissingResourceException e) {
|
||||||
if (!silent)
|
if (!silent)
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -95,12 +101,12 @@ public class Localizer {
|
|||||||
|
|
||||||
silent = false;
|
silent = false;
|
||||||
|
|
||||||
formatter.setLocale(english ? Locale.ENGLISH : locale);
|
formatter.setLocale(english || forcedEnglish ? Locale.ENGLISH : locale);
|
||||||
|
|
||||||
String formattedMessage = "CHAR ENCODING ERROR";
|
String formattedMessage = "CHAR ENCODING ERROR";
|
||||||
final String[] charsets = { "ISO-8859-1", "UTF-8" };
|
final String[] charsets = { "ISO-8859-1", "UTF-8" };
|
||||||
//Support non-English-standard characters
|
//Support non-English-standard characters
|
||||||
String detectedCharset = charset(english ? englishBundle.getString(key) : resourceBundle.getString(key), charsets);
|
String detectedCharset = charset(english || forcedEnglish ? englishBundle.getString(key) : resourceBundle.getString(key), charsets);
|
||||||
|
|
||||||
final int argLength = messageArguments.length;
|
final int argLength = messageArguments.length;
|
||||||
Object[] syncEncodingMessageArguments = new Object[argLength];
|
Object[] syncEncodingMessageArguments = new Object[argLength];
|
||||||
@@ -149,7 +155,7 @@ public class Localizer {
|
|||||||
englishBundle = ResourceBundle.getBundle("en-US", new Locale("en", "US"), loader);
|
englishBundle = ResourceBundle.getBundle("en-US", new Locale("en", "US"), loader);
|
||||||
} catch (NullPointerException | MissingResourceException e) {
|
} catch (NullPointerException | MissingResourceException e) {
|
||||||
//If the language can't be loaded, default to US English
|
//If the language can't be loaded, default to US English
|
||||||
resourceBundle = ResourceBundle.getBundle("en-US", new Locale("en", "US"), loader);
|
resourceBundle = ResourceBundle.getBundle("en-US", new Locale("en_US"), loader);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>1.6.50-SNAPSHOT</version>
|
<version>1.6.54-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-game</artifactId>
|
<artifactId>forge-game</artifactId>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import forge.game.card.CardPredicates;
|
|||||||
import forge.game.card.CardState;
|
import forge.game.card.CardState;
|
||||||
import forge.game.card.CardView;
|
import forge.game.card.CardView;
|
||||||
import forge.game.card.IHasCardView;
|
import forge.game.card.IHasCardView;
|
||||||
|
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.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
@@ -34,6 +35,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
/** The host card. */
|
/** The host card. */
|
||||||
protected Card hostCard;
|
protected Card hostCard;
|
||||||
protected CardState cardState = null;
|
protected CardState cardState = null;
|
||||||
|
protected KeywordInterface keyword = null;
|
||||||
|
|
||||||
/** The map params. */
|
/** The map params. */
|
||||||
protected Map<String, String> originalMapParams = Maps.newHashMap(),
|
protected Map<String, String> originalMapParams = Maps.newHashMap(),
|
||||||
@@ -55,15 +57,13 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
/** Keys of descriptive (text) parameters. */
|
/** Keys of descriptive (text) parameters. */
|
||||||
private static final ImmutableList<String> descriptiveKeys = ImmutableList.<String>builder()
|
private static final ImmutableList<String> descriptiveKeys = ImmutableList.<String>builder()
|
||||||
.add("Description", "SpellDescription", "StackDescription", "TriggerDescription").build();
|
.add("Description", "SpellDescription", "StackDescription", "TriggerDescription").build();
|
||||||
/** Keys to be followed as SVar names when changing text. */
|
|
||||||
private static final ImmutableList<String> mutableKeys = ImmutableList.<String>builder()
|
|
||||||
.add("AddAbility").build();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keys that should not changed
|
* Keys that should not changed
|
||||||
*/
|
*/
|
||||||
private static final ImmutableList<String> noChangeKeys = ImmutableList.<String>builder()
|
private static final ImmutableList<String> noChangeKeys = ImmutableList.<String>builder()
|
||||||
.add("TokenScript", "LegacyImage", "TokenImage", "NewName", "ChooseFromList").build();
|
.add("TokenScript", "LegacyImage", "TokenImage", "NewName", "ChooseFromList")
|
||||||
|
.add("AddAbility").build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -140,6 +140,14 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
this.hostCard = c;
|
this.hostCard = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KeywordInterface getKeyword() {
|
||||||
|
return this.keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyword(final KeywordInterface kw) {
|
||||||
|
this.keyword = kw;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* isSecondary.
|
* isSecondary.
|
||||||
@@ -259,6 +267,18 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
}
|
}
|
||||||
if (params.containsKey("Revolt")) {
|
if (params.containsKey("Revolt")) {
|
||||||
if ("True".equalsIgnoreCase(params.get("Revolt")) != hostController.hasRevolt()) return false;
|
if ("True".equalsIgnoreCase(params.get("Revolt")) != hostController.hasRevolt()) return false;
|
||||||
|
else if ("None".equalsIgnoreCase(params.get("Revolt"))) {
|
||||||
|
boolean none = true;
|
||||||
|
for (Player p : game.getRegisteredPlayers()) {
|
||||||
|
if (p.hasRevolt()) {
|
||||||
|
none = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!none) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (params.containsKey("Desert")) {
|
if (params.containsKey("Desert")) {
|
||||||
if ("True".equalsIgnoreCase(params.get("Desert")) != hostController.hasDesert()) return false;
|
if ("True".equalsIgnoreCase(params.get("Desert")) != hostController.hasDesert()) return false;
|
||||||
@@ -422,6 +442,16 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
if (!Expressions.compare(sVar, svarOperator, operandValue)) {
|
if (!Expressions.compare(sVar, svarOperator, operandValue)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (hasParam("CheckSecondSVar")) {
|
||||||
|
final int sVar2 = AbilityUtils.calculateAmount(this.hostCard, getParam("CheckSecondSVar"), this);
|
||||||
|
final String comparator2 = getParamOrDefault("SecondSVarCompare", "GE1");
|
||||||
|
final String svarOperator2 = comparator2.substring(0, 2);
|
||||||
|
final String svarOperand2 = comparator2.substring(2);
|
||||||
|
final int operandValue2 = AbilityUtils.calculateAmount(this.hostCard, svarOperand2, this);
|
||||||
|
if (!Expressions.compare(sVar2, svarOperator2, operandValue2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("ManaSpent")) {
|
if (params.containsKey("ManaSpent")) {
|
||||||
@@ -489,11 +519,6 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
} else if (descriptiveKeys.contains(key)) {
|
} else if (descriptiveKeys.contains(key)) {
|
||||||
// change descriptions differently
|
// change descriptions differently
|
||||||
newValue = AbilityUtils.applyDescriptionTextChangeEffects(value, this);
|
newValue = AbilityUtils.applyDescriptionTextChangeEffects(value, this);
|
||||||
} else if (mutableKeys.contains(key)) {
|
|
||||||
// follow SVar and change it
|
|
||||||
final String originalSVarValue = hostCard.getSVar(value);
|
|
||||||
hostCard.changeSVar(value, AbilityUtils.applyAbilityTextChangeEffects(originalSVarValue, this));
|
|
||||||
newValue = null;
|
|
||||||
} else if (this.getHostCard().hasSVar(value)) {
|
} else if (this.getHostCard().hasSVar(value)) {
|
||||||
// don't change literal SVar names!
|
// don't change literal SVar names!
|
||||||
newValue = null;
|
newValue = null;
|
||||||
@@ -652,6 +677,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
copy.setCardState(cardState);
|
copy.setCardState(cardState);
|
||||||
// dont use setHostCard to not trigger the not copied parts yet
|
// dont use setHostCard to not trigger the not copied parts yet
|
||||||
copy.hostCard = host;
|
copy.hostCard = host;
|
||||||
|
copy.keyword = this.keyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public List<Object> getTriggerRemembered();
|
abstract public List<Object> getTriggerRemembered();
|
||||||
|
|||||||
@@ -202,6 +202,11 @@ public class ForgeScript {
|
|||||||
return sa.hasParam("Nightbound");
|
return sa.hasParam("Nightbound");
|
||||||
} else if (property.equals("paidPhyrexianMana")) {
|
} else if (property.equals("paidPhyrexianMana")) {
|
||||||
return sa.getSpendPhyrexianMana();
|
return sa.getSpendPhyrexianMana();
|
||||||
|
} else if (property.startsWith("ManaSpent")) {
|
||||||
|
String[] k = property.split(" ", 2);
|
||||||
|
String comparator = k[1].substring(0, 2);
|
||||||
|
int y = AbilityUtils.calculateAmount(sa.getHostCard(), k[1].substring(2), sa);
|
||||||
|
return Expressions.compare(sa.getPayingMana().size(), comparator, y);
|
||||||
} else if (property.startsWith("ManaFrom")) {
|
} else if (property.startsWith("ManaFrom")) {
|
||||||
final String fromWhat = property.substring(8);
|
final String fromWhat = property.substring(8);
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
@@ -247,7 +252,7 @@ public class ForgeScript {
|
|||||||
} else if (property.startsWith("cmc")) {
|
} else if (property.startsWith("cmc")) {
|
||||||
int y = 0;
|
int y = 0;
|
||||||
// spell was on the stack
|
// spell was on the stack
|
||||||
if (sa.getCardState().getCard().isInZone(ZoneType.Stack)) {
|
if (sa.getHostCard().isInZone(ZoneType.Stack)) {
|
||||||
y = sa.getHostCard().getCMC();
|
y = sa.getHostCard().getCMC();
|
||||||
} else {
|
} else {
|
||||||
y = sa.getPayCosts().getTotalMana().getCMC();
|
y = sa.getPayCosts().getTotalMana().getCMC();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -47,6 +48,7 @@ import forge.game.ability.AbilityKey;
|
|||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
|
import forge.game.card.CardDamageHistory;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
@@ -78,6 +80,7 @@ import forge.trackable.Tracker;
|
|||||||
import forge.util.Aggregates;
|
import forge.util.Aggregates;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import forge.util.Visitor;
|
import forge.util.Visitor;
|
||||||
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the state of a <i>single game</i>, a new instance is created for each game.
|
* Represents the state of a <i>single game</i>, a new instance is created for each game.
|
||||||
@@ -119,10 +122,14 @@ public class Game {
|
|||||||
|
|
||||||
private Table<CounterType, Player, List<Pair<Card, Integer>>> countersAddedThisTurn = HashBasedTable.create();
|
private Table<CounterType, Player, List<Pair<Card, Integer>>> countersAddedThisTurn = HashBasedTable.create();
|
||||||
|
|
||||||
|
private FCollection<CardDamageHistory> globalDamageHistory = new FCollection<>();
|
||||||
|
private IdentityHashMap<Pair<Integer, Boolean>, Pair<Card, GameEntity>> damageThisTurnLKI = new IdentityHashMap<>();
|
||||||
|
|
||||||
private Map<Player, Card> topLibsCast = Maps.newHashMap();
|
private Map<Player, Card> topLibsCast = Maps.newHashMap();
|
||||||
private Map<Card, Integer> facedownWhileCasting = Maps.newHashMap();
|
private Map<Card, Integer> facedownWhileCasting = Maps.newHashMap();
|
||||||
|
|
||||||
private Player monarch = null;
|
private Player monarch = null;
|
||||||
|
private Player initiative = null;
|
||||||
private Player monarchBeginTurn = null;
|
private Player monarchBeginTurn = null;
|
||||||
private Player startingPlayer;
|
private Player startingPlayer;
|
||||||
|
|
||||||
@@ -173,6 +180,13 @@ public class Game {
|
|||||||
this.monarchBeginTurn = monarchBeginTurn;
|
this.monarchBeginTurn = monarchBeginTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Player getHasInitiative() {
|
||||||
|
return initiative;
|
||||||
|
}
|
||||||
|
public void setHasInitiative(final Player p ) {
|
||||||
|
initiative = p;
|
||||||
|
}
|
||||||
|
|
||||||
public CardCollectionView getLastStateBattlefield() {
|
public CardCollectionView getLastStateBattlefield() {
|
||||||
return lastStateBattlefield;
|
return lastStateBattlefield;
|
||||||
}
|
}
|
||||||
@@ -243,7 +257,7 @@ public class Game {
|
|||||||
if (c == null) {
|
if (c == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return changeZoneLKIInfo.containsKey(c.getId()) ? changeZoneLKIInfo.get(c.getId()) : c;
|
return changeZoneLKIInfo.getOrDefault(c.getId(), c);
|
||||||
}
|
}
|
||||||
public final void clearChangeZoneLKIInfo() {
|
public final void clearChangeZoneLKIInfo() {
|
||||||
changeZoneLKIInfo.clear();
|
changeZoneLKIInfo.clear();
|
||||||
@@ -846,6 +860,18 @@ public class Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p.hasInitiative()) {
|
||||||
|
// The third way to take the initiative is if the player who currently has the initiative leaves the game.
|
||||||
|
// When that happens, the player whose turn it is takes the initiative.
|
||||||
|
// If the player who has the initiative leaves the game on their own turn,
|
||||||
|
// or the active player left the game at the same time, the next player in turn order takes the initiative.
|
||||||
|
if (p.equals(getPhaseHandler().getPlayerTurn())) {
|
||||||
|
getAction().takeInitiative(getNextPlayerAfter(p), null);
|
||||||
|
} else {
|
||||||
|
getAction().takeInitiative(getPhaseHandler().getPlayerTurn(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove leftover items from
|
// Remove leftover items from
|
||||||
getStack().removeInstancesControlledBy(p);
|
getStack().removeInstancesControlledBy(p);
|
||||||
|
|
||||||
@@ -1068,6 +1094,7 @@ public class Game {
|
|||||||
|
|
||||||
public void onCleanupPhase() {
|
public void onCleanupPhase() {
|
||||||
clearCounterAddedThisTurn();
|
clearCounterAddedThisTurn();
|
||||||
|
clearGlobalDamageHistory();
|
||||||
// some cards need this info updated even after a player lost, so don't skip them
|
// some cards need this info updated even after a player lost, so don't skip them
|
||||||
for (Player player : getRegisteredPlayers()) {
|
for (Player player : getRegisteredPlayers()) {
|
||||||
player.onCleanupPhase();
|
player.onCleanupPhase();
|
||||||
@@ -1109,6 +1136,48 @@ public class Game {
|
|||||||
countersAddedThisTurn.clear();
|
countersAddedThisTurn.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the damage instances done this turn.
|
||||||
|
* @param isCombat if true only combat damage matters, pass null for both
|
||||||
|
* @param anyIsEnough if true returns early once result has an entry
|
||||||
|
* @param validSourceCard
|
||||||
|
* @param validTargetEntity
|
||||||
|
* @param source
|
||||||
|
* @param sourceController
|
||||||
|
* @param ctb
|
||||||
|
* @return List<Integer> for each source
|
||||||
|
*/
|
||||||
|
public List<Integer> getDamageDoneThisTurn(Boolean isCombat, boolean anyIsEnough, String validSourceCard, String validTargetEntity, Card source, Player sourceController, CardTraitBase ctb) {
|
||||||
|
final List<Integer> dmgList = Lists.newArrayList();
|
||||||
|
for (CardDamageHistory cdh : globalDamageHistory) {
|
||||||
|
int dmg = cdh.getDamageDoneThisTurn(isCombat, anyIsEnough, validSourceCard, validTargetEntity, source, sourceController, ctb);
|
||||||
|
if (dmg == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dmgList.add(dmg);
|
||||||
|
|
||||||
|
if (anyIsEnough) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmgList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGlobalDamageHistory(CardDamageHistory cdh, Pair<Integer, Boolean> dmg, Card source, GameEntity target) {
|
||||||
|
globalDamageHistory.add(cdh);
|
||||||
|
damageThisTurnLKI.put(dmg, Pair.of(source, target));
|
||||||
|
}
|
||||||
|
public void clearGlobalDamageHistory() {
|
||||||
|
globalDamageHistory.clear();
|
||||||
|
damageThisTurnLKI.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pair<Card, GameEntity> getDamageLKI(Pair<Integer, Boolean> dmg) {
|
||||||
|
return damageThisTurnLKI.get(dmg);
|
||||||
|
}
|
||||||
|
|
||||||
public Card getTopLibForPlayer(Player P) {
|
public Card getTopLibForPlayer(Player P) {
|
||||||
return topLibsCast.get(P);
|
return topLibsCast.get(P);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import java.util.Set;
|
|||||||
import forge.util.*;
|
import forge.util.*;
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.ComparisonChain;
|
import com.google.common.collect.ComparisonChain;
|
||||||
@@ -37,6 +36,7 @@ import com.google.common.collect.Maps;
|
|||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.Multimaps;
|
import com.google.common.collect.Multimaps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.common.collect.Table;
|
||||||
|
|
||||||
import forge.GameCommand;
|
import forge.GameCommand;
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
@@ -66,6 +66,7 @@ import forge.game.event.GameEventGameStarted;
|
|||||||
import forge.game.event.GameEventScry;
|
import forge.game.event.GameEventScry;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
|
import forge.game.keyword.KeywordsChange;
|
||||||
import forge.game.mulligan.MulliganService;
|
import forge.game.mulligan.MulliganService;
|
||||||
import forge.game.player.GameLossReason;
|
import forge.game.player.GameLossReason;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
@@ -161,15 +162,33 @@ public class GameAction {
|
|||||||
// need to check before it enters
|
// need to check before it enters
|
||||||
if (c.isAura() && !c.isAttachedToEntity() && toBattlefield && (zoneFrom == null || !zoneFrom.is(ZoneType.Stack))) {
|
if (c.isAura() && !c.isAttachedToEntity() && toBattlefield && (zoneFrom == null || !zoneFrom.is(ZoneType.Stack))) {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
|
try {
|
||||||
if (Iterables.any(game.getPlayers(), PlayerPredicates.canBeAttached(c, null))) {
|
if (Iterables.any(game.getPlayers(), PlayerPredicates.canBeAttached(c, null))) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
else if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateBattlefield), CardPredicates.canBeAttached(c, null))) {
|
} catch (Exception e1) {
|
||||||
|
found = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
try {
|
||||||
|
if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateBattlefield), CardPredicates.canBeAttached(c, null))) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
else if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateGraveyard), CardPredicates.canBeAttached(c, null))) {
|
} catch (Exception e2) {
|
||||||
|
found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
try {
|
||||||
|
if (Iterables.any((CardCollectionView) params.get(AbilityKey.LastStateGraveyard), CardPredicates.canBeAttached(c, null))) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
|
} catch (Exception e3) {
|
||||||
|
found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
c.clearControllers();
|
c.clearControllers();
|
||||||
if (c.removeChangedState()) {
|
if (c.removeChangedState()) {
|
||||||
@@ -233,13 +252,6 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the temporary Dash/Blitz SVar when the card leaves the battlefield
|
|
||||||
// Clean up the temporary AtEOT SVar
|
|
||||||
String endofTurn = c.getSVar("EndOfTurnLeavePlay");
|
|
||||||
if (fromBattlefield && (endofTurn.equals("Dash") || endofTurn.equals("Blitz") || endofTurn.equals("AtEOT"))) {
|
|
||||||
c.removeSVar("EndOfTurnLeavePlay");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fromBattlefield && !toBattlefield) {
|
if (fromBattlefield && !toBattlefield) {
|
||||||
c.getController().setRevolt(true);
|
c.getController().setRevolt(true);
|
||||||
}
|
}
|
||||||
@@ -253,10 +265,6 @@ public class GameAction {
|
|||||||
lastKnownInfo = CardUtil.getLKICopy(c);
|
lastKnownInfo = CardUtil.getLKICopy(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!suppress) {
|
|
||||||
copied.setTimestamp(game.getNextTimestamp());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lastKnownInfo.hasKeyword("Counters remain on CARDNAME as it moves to any zone other than a player's hand or library.")) {
|
if (!lastKnownInfo.hasKeyword("Counters remain on CARDNAME as it moves to any zone other than a player's hand or library.")) {
|
||||||
copied.clearCounters();
|
copied.clearCounters();
|
||||||
}
|
}
|
||||||
@@ -281,6 +289,8 @@ public class GameAction {
|
|||||||
copied = CardFactory.copyCard(c, false);
|
copied = CardFactory.copyCard(c, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copied.setTimestamp(c.getTimestamp());
|
||||||
|
|
||||||
if (zoneTo.is(ZoneType.Stack)) {
|
if (zoneTo.is(ZoneType.Stack)) {
|
||||||
// when moving to stack, copy changed card information
|
// when moving to stack, copy changed card information
|
||||||
copied.setChangedCardColors(c.getChangedCardColorsTable());
|
copied.setChangedCardColors(c.getChangedCardColorsTable());
|
||||||
@@ -293,7 +303,6 @@ public class GameAction {
|
|||||||
copied.setDrawnThisTurn(c.getDrawnThisTurn());
|
copied.setDrawnThisTurn(c.getDrawnThisTurn());
|
||||||
|
|
||||||
copied.copyChangedTextFrom(c);
|
copied.copyChangedTextFrom(c);
|
||||||
copied.setTimestamp(c.getTimestamp());
|
|
||||||
|
|
||||||
// clean up changes that come from its own static abilities
|
// clean up changes that come from its own static abilities
|
||||||
copied.cleanupCopiedChangesFrom(c);
|
copied.cleanupCopiedChangesFrom(c);
|
||||||
@@ -305,15 +314,20 @@ public class GameAction {
|
|||||||
|
|
||||||
// copy bestow timestamp
|
// copy bestow timestamp
|
||||||
copied.setBestowTimestamp(c.getBestowTimestamp());
|
copied.setBestowTimestamp(c.getBestowTimestamp());
|
||||||
|
|
||||||
|
if (cause != null && cause.isSpell() && c.equals(cause.getHostCard())) {
|
||||||
|
copied.setCastSA(cause);
|
||||||
|
KeywordInterface kw = cause.getKeyword();
|
||||||
|
if (kw != null) {
|
||||||
|
copied.addKeywordForStaticAbility(kw);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// when a card leaves the battlefield, ensure it's in its original state
|
// when a card leaves the battlefield, ensure it's in its original state
|
||||||
// (we need to do this on the object before copying it, or it won't work correctly e.g.
|
// (we need to do this on the object before copying it, or it won't work correctly e.g.
|
||||||
// on Transformed objects)
|
// on Transformed objects)
|
||||||
copied.setState(CardStateName.Original, false);
|
copied.setState(CardStateName.Original, false);
|
||||||
copied.setBackSide(false);
|
copied.setBackSide(false);
|
||||||
|
|
||||||
// reset timestamp in changezone effects so they have same timestamp if ETB simultaneously
|
|
||||||
copied.setTimestamp(game.getNextTimestamp());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
copied.setUnearthed(c.isUnearthed());
|
copied.setUnearthed(c.isUnearthed());
|
||||||
@@ -337,7 +351,7 @@ public class GameAction {
|
|||||||
CardCollectionView comCards = c.getOwner().getCardsIn(ZoneType.Command);
|
CardCollectionView comCards = c.getOwner().getCardsIn(ZoneType.Command);
|
||||||
for (final Card effCard : comCards) {
|
for (final Card effCard : comCards) {
|
||||||
for (final ReplacementEffect re : effCard.getReplacementEffects()) {
|
for (final ReplacementEffect re : effCard.getReplacementEffects()) {
|
||||||
if (re.hasSVar("CommanderMoveReplacement") && effCard.getEffectSource().getName().equals(c.getRealCommander().getName())) {
|
if (re.hasParam("CommanderMoveReplacement") && c.getMergedCards().contains(effCard.getEffectSource())) {
|
||||||
commanderEffect = effCard;
|
commanderEffect = effCard;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -345,10 +359,12 @@ public class GameAction {
|
|||||||
if (commanderEffect != null) break;
|
if (commanderEffect != null) break;
|
||||||
}
|
}
|
||||||
// Disable the commander replacement effect
|
// Disable the commander replacement effect
|
||||||
|
if (commanderEffect != null) {
|
||||||
for (final ReplacementEffect re : commanderEffect.getReplacementEffects()) {
|
for (final ReplacementEffect re : commanderEffect.getReplacementEffects()) {
|
||||||
re.setSuppressed(true);
|
re.setSuppressed(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (zoneFrom == null) {
|
if (zoneFrom == null) {
|
||||||
copied.getOwner().addInboundToken(copied);
|
copied.getOwner().addInboundToken(copied);
|
||||||
@@ -365,7 +381,7 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams);
|
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams);
|
||||||
if (repres != ReplacementResult.NotReplaced) {
|
if (repres != ReplacementResult.NotReplaced && repres != ReplacementResult.Updated) {
|
||||||
// reset failed manifested Cards back to original
|
// reset failed manifested Cards back to original
|
||||||
if (c.isManifested() && !c.isInPlay()) {
|
if (c.isManifested() && !c.isInPlay()) {
|
||||||
c.forceTurnFaceUp();
|
c.forceTurnFaceUp();
|
||||||
@@ -395,6 +411,11 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!zoneTo.is(ZoneType.Stack) && !suppress) {
|
||||||
|
// reset timestamp in changezone effects so they have same timestamp if ETB simultaneously
|
||||||
|
copied.setTimestamp(game.getNextTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
copied.getOwner().removeInboundToken(copied);
|
copied.getOwner().removeInboundToken(copied);
|
||||||
|
|
||||||
// Aura entering as Copy from stack
|
// Aura entering as Copy from stack
|
||||||
@@ -489,6 +510,37 @@ public class GameAction {
|
|||||||
if (!zoneTo.is(ZoneType.Exile) && !zoneTo.is(ZoneType.Stack)) {
|
if (!zoneTo.is(ZoneType.Exile) && !zoneTo.is(ZoneType.Stack)) {
|
||||||
c.cleanupExiledWith();
|
c.cleanupExiledWith();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 400.7a Effects from static abilities that give a permanent spell on the stack an ability
|
||||||
|
// that allows it to be cast for an alternative cost continue to apply to the permanent that spell becomes.
|
||||||
|
if (zoneFrom.is(ZoneType.Stack) && toBattlefield) {
|
||||||
|
List<KeywordInterface> newKw = Lists.newArrayList();
|
||||||
|
for (Table.Cell<Long, Long, KeywordsChange> cell : c.getChangedCardKeywords().cellSet()) {
|
||||||
|
// comes from a static ability
|
||||||
|
if (cell.getColumnKey() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (KeywordInterface ki : cell.getValue().getKeywords()) {
|
||||||
|
boolean keepKeyword = false;
|
||||||
|
for (SpellAbility sa : ki.getAbilities()) {
|
||||||
|
if (!sa.isSpell()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sa.getAlternativeCost() != null) {
|
||||||
|
keepKeyword = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keepKeyword) {
|
||||||
|
ki.setHostCard(copied);
|
||||||
|
newKw.add(ki);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!newKw.isEmpty()) {
|
||||||
|
copied.addChangedCardKeywordsInternal(newKw, null, false, copied.getTimestamp(), 0, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if an adventureCard is put from Stack somewhere else, need to reset to Original State
|
// if an adventureCard is put from Stack somewhere else, need to reset to Original State
|
||||||
@@ -498,15 +550,6 @@ public class GameAction {
|
|||||||
|
|
||||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||||
|
|
||||||
// need to suspend cards own replacement effects
|
|
||||||
if (!suppress) {
|
|
||||||
if (toBattlefield && !copied.getEtbCounters().isEmpty()) {
|
|
||||||
for (final ReplacementEffect re : copied.getReplacementEffects()) {
|
|
||||||
re.setSuppressed(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mergedCards != null) {
|
if (mergedCards != null) {
|
||||||
// Move components of merged permanent here
|
// Move components of merged permanent here
|
||||||
// Also handle 723.3e and 903.9a
|
// Also handle 723.3e and 903.9a
|
||||||
@@ -553,14 +596,9 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do ETB counters after zone add
|
// do ETB counters after zone add
|
||||||
if (!suppress) {
|
if (!suppress && toBattlefield && !copied.getEtbCounters().isEmpty()) {
|
||||||
if (toBattlefield) {
|
game.getTriggerHandler().registerActiveTrigger(copied, false);
|
||||||
copied.putEtbCounters(table);
|
copied.putEtbCounters(table);
|
||||||
// enable replacement effects again
|
|
||||||
for (final ReplacementEffect re : copied.getReplacementEffects()) {
|
|
||||||
re.setSuppressed(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
copied.clearEtbCounters();
|
copied.clearEtbCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,7 +619,7 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.replaceCounterEffect(game, null, true);
|
table.replaceCounterEffect(game, null, true, true, params);
|
||||||
|
|
||||||
// Need to apply any static effects to produce correct triggers
|
// Need to apply any static effects to produce correct triggers
|
||||||
checkStaticAbilities();
|
checkStaticAbilities();
|
||||||
@@ -1059,6 +1097,24 @@ public class GameAction {
|
|||||||
return holdCheckingStaticAbilities;
|
return holdCheckingStaticAbilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This doesn't check layers or if the ability gets removed by other effects
|
||||||
|
public boolean hasStaticAbilityAffectingZone(ZoneType zone, StaticAbilityLayer layer) {
|
||||||
|
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||||
|
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||||
|
if (!stAb.getParam("Mode").equals("Continuous") || stAb.isSuppressed() || !stAb.checkConditions()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (layer != null && !stAb.getLayers().contains(layer)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ZoneType.listValueOf(stAb.getParamOrDefault("AffectedZone", ZoneType.Battlefield.toString())).contains(zone)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public final void checkStaticAbilities() {
|
public final void checkStaticAbilities() {
|
||||||
checkStaticAbilities(true);
|
checkStaticAbilities(true);
|
||||||
}
|
}
|
||||||
@@ -1183,6 +1239,9 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// preList means that this is run by a pre Check with LKI objects
|
||||||
|
// in that case Always trigger should not Run
|
||||||
|
if (preList.isEmpty()) {
|
||||||
for (Player p : game.getPlayers()) {
|
for (Player p : game.getPlayers()) {
|
||||||
for (Card c : p.getCardsIn(ZoneType.Battlefield).threadSafeIterable()) {
|
for (Card c : p.getCardsIn(ZoneType.Battlefield).threadSafeIterable()) {
|
||||||
if (!c.getController().equals(p)) {
|
if (!c.getController().equals(p)) {
|
||||||
@@ -1200,9 +1259,6 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.Always, runParams, false);
|
||||||
|
|
||||||
@@ -1217,6 +1273,8 @@ public class GameAction {
|
|||||||
c.updateAbilityTextForView(); // only update keywords and text for view to avoid flickering
|
c.updateAbilityTextForView(); // only update keywords and text for view to avoid flickering
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO filter out old copies from zone change
|
||||||
|
|
||||||
if (runEvents && !affectedCards.isEmpty()) {
|
if (runEvents && !affectedCards.isEmpty()) {
|
||||||
game.fireEvent(new GameEventCardStatsChanged(affectedCards));
|
game.fireEvent(new GameEventCardStatsChanged(affectedCards));
|
||||||
}
|
}
|
||||||
@@ -1293,33 +1351,13 @@ public class GameAction {
|
|||||||
noRegCreats.add(c);
|
noRegCreats.add(c);
|
||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
} else if (c.hasKeyword("CARDNAME can't be destroyed by lethal damage unless lethal damage dealt by a single source is marked on it.")) {
|
} else if (c.hasKeyword("CARDNAME can't be destroyed by lethal damage unless lethal damage dealt by a single source is marked on it.")) {
|
||||||
// merge entries with same source
|
if (c.getLethal() <= c.getMaxDamageFromSource() || c.hasBeenDealtDeathtouchDamage()) {
|
||||||
List<Integer> dmgList = Lists.newArrayList();
|
|
||||||
List<Pair<Card, Integer>> remainingDamaged = Lists.newArrayList(c.getReceivedDamageFromThisTurn());
|
|
||||||
while (!remainingDamaged.isEmpty()) {
|
|
||||||
Pair <Card, Integer> damaged = remainingDamaged.get(0);
|
|
||||||
int sum = damaged.getRight();
|
|
||||||
remainingDamaged.remove(damaged);
|
|
||||||
for (Pair<Card, Integer> other : Lists.newArrayList(remainingDamaged)) {
|
|
||||||
if (other.getLeft().equalsWithTimestamp(damaged.getLeft())) {
|
|
||||||
sum += other.getRight();
|
|
||||||
// once it got counted keep it out
|
|
||||||
remainingDamaged.remove(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dmgList.add(sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Integer dmg : dmgList) {
|
|
||||||
if (c.getLethal() <= dmg.intValue() || c.hasBeenDealtDeathtouchDamage()) {
|
|
||||||
if (desCreats == null) {
|
if (desCreats == null) {
|
||||||
desCreats = new CardCollection();
|
desCreats = new CardCollection();
|
||||||
}
|
}
|
||||||
desCreats.add(c);
|
desCreats.add(c);
|
||||||
c.setHasBeenDealtDeathtouchDamage(false);
|
c.setHasBeenDealtDeathtouchDamage(false);
|
||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Rule 704.5g - Destroy due to lethal damage
|
// Rule 704.5g - Destroy due to lethal damage
|
||||||
@@ -1546,7 +1584,7 @@ public class GameAction {
|
|||||||
c.getGame().getTracker().flush();
|
c.getGame().getTracker().flush();
|
||||||
|
|
||||||
c.setMoveToCommandZone(false);
|
c.setMoveToCommandZone(false);
|
||||||
if (c.getOwner().getController().confirmAction(c.getFirstSpellAbility(), PlayerActionConfirmMode.ChangeZoneToAltDestination, c.getName() + ": If a commander is in a graveyard or in exile and that card was put into that zone since the last time state-based actions were checked, its owner may put it into the command zone.")) {
|
if (c.getOwner().getController().confirmAction(c.getFirstSpellAbility(), PlayerActionConfirmMode.ChangeZoneToAltDestination, c.getName() + ": If a commander is in a graveyard or in exile and that card was put into that zone since the last time state-based actions were checked, its owner may put it into the command zone.", null)) {
|
||||||
moveTo(c.getOwner().getZone(ZoneType.Command), c, null);
|
moveTo(c.getOwner().getZone(ZoneType.Command), c, null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2203,6 +2241,32 @@ public class GameAction {
|
|||||||
game.getTriggerHandler().runTrigger(TriggerType.BecomeMonarch, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.BecomeMonarch, runParams, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void takeInitiative(final Player p, final String set) {
|
||||||
|
final Player previous = game.getHasInitiative();
|
||||||
|
if (p == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p.equals(previous)) {
|
||||||
|
if (previous != null) {
|
||||||
|
previous.removeInitiativeEffect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.hasLost()) { // the person who should take initiative is gone, it goes to next player
|
||||||
|
takeInitiative(game.getNextPlayerAfter(p), set);
|
||||||
|
}
|
||||||
|
|
||||||
|
game.setHasInitiative(p);
|
||||||
|
p.createInitiativeEffect(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can take the initiative even if you already have it
|
||||||
|
// Run triggers
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Player, p);
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.TakesInitiative, runParams, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Make scry an action function so that it can be used for mulligans (with a null cause)
|
// Make scry an action function so that it can be used for mulligans (with a null cause)
|
||||||
// Assumes that the list of players is in APNAP order, which should be the case
|
// Assumes that the list of players is in APNAP order, which should be the case
|
||||||
// Optional here as well to handle the way that mulligans do the choice
|
// Optional here as well to handle the way that mulligans do the choice
|
||||||
@@ -2271,13 +2335,16 @@ public class GameAction {
|
|||||||
final Player p = e.getKey();
|
final Player p = e.getKey();
|
||||||
final CardCollection toTop = e.getValue().getLeft();
|
final CardCollection toTop = e.getValue().getLeft();
|
||||||
final CardCollection toBottom = e.getValue().getRight();
|
final CardCollection toBottom = e.getValue().getRight();
|
||||||
|
int numLookedAt = 0;
|
||||||
if (toTop != null) {
|
if (toTop != null) {
|
||||||
|
numLookedAt += toTop.size();
|
||||||
Collections.reverse(toTop); // reverse to get the correct order
|
Collections.reverse(toTop); // reverse to get the correct order
|
||||||
for (Card c : toTop) {
|
for (Card c : toTop) {
|
||||||
moveToLibrary(c, cause, null);
|
moveToLibrary(c, cause, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (toBottom != null) {
|
if (toBottom != null) {
|
||||||
|
numLookedAt += toBottom.size();
|
||||||
for (Card c : toBottom) {
|
for (Card c : toBottom) {
|
||||||
moveToBottomOfLibrary(c, cause, null);
|
moveToBottomOfLibrary(c, cause, null);
|
||||||
}
|
}
|
||||||
@@ -2287,6 +2354,7 @@ public class GameAction {
|
|||||||
// set up triggers (but not actually do them until later)
|
// set up triggers (but not actually do them until later)
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
runParams.put(AbilityKey.Player, p);
|
runParams.put(AbilityKey.Player, p);
|
||||||
|
runParams.put(AbilityKey.ScryNum, numLookedAt);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.Scry, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.Scry, runParams, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2308,6 +2376,7 @@ public class GameAction {
|
|||||||
game.getReplacementHandler().runReplaceDamage(isCombat, damageMap, preventMap, counterTable, cause);
|
game.getReplacementHandler().runReplaceDamage(isCombat, damageMap, preventMap, counterTable, cause);
|
||||||
|
|
||||||
Map<Card, Integer> lethalDamage = Maps.newHashMap();
|
Map<Card, Integer> lethalDamage = Maps.newHashMap();
|
||||||
|
Map<Integer, Card> lkiCache = Maps.newHashMap();
|
||||||
|
|
||||||
// Actually deal damage according to replaced damage map
|
// Actually deal damage according to replaced damage map
|
||||||
for (Map.Entry<Card, Map<GameEntity, Integer>> et : damageMap.rowMap().entrySet()) {
|
for (Map.Entry<Card, Map<GameEntity, Integer>> et : damageMap.rowMap().entrySet()) {
|
||||||
@@ -2334,6 +2403,8 @@ public class GameAction {
|
|||||||
|
|
||||||
e.setValue(Integer.valueOf(e.getKey().addDamageAfterPrevention(e.getValue(), sourceLKI, isCombat, counterTable)));
|
e.setValue(Integer.valueOf(e.getKey().addDamageAfterPrevention(e.getValue(), sourceLKI, isCombat, counterTable)));
|
||||||
sum += e.getValue();
|
sum += e.getValue();
|
||||||
|
|
||||||
|
sourceLKI.getDamageHistory().registerDamage(e.getValue(), isCombat, sourceLKI, e.getKey(), lkiCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sum > 0 && sourceLKI.hasKeyword(Keyword.LIFELINK)) {
|
if (sum > 0 && sourceLKI.hasKeyword(Keyword.LIFELINK)) {
|
||||||
|
|||||||
@@ -42,17 +42,12 @@ import forge.game.player.PlayerController;
|
|||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.replacement.ReplacementHandler;
|
import forge.game.replacement.ReplacementHandler;
|
||||||
import forge.game.replacement.ReplacementLayer;
|
import forge.game.replacement.ReplacementLayer;
|
||||||
import forge.game.spellability.AbilityManaPart;
|
import forge.game.spellability.*;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.staticability.StaticAbilityLayer;
|
||||||
import forge.game.spellability.AlternativeCost;
|
|
||||||
import forge.game.spellability.OptionalCost;
|
|
||||||
import forge.game.spellability.OptionalCostValue;
|
|
||||||
import forge.game.spellability.Spell;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
|
||||||
import forge.game.spellability.SpellAbilityRestriction;
|
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerHandler;
|
import forge.game.trigger.TriggerHandler;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
|
import forge.game.zone.Zone;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Lang;
|
import forge.util.Lang;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
@@ -91,7 +86,11 @@ public final class GameActionUtil {
|
|||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
final Game game = source.getGame();
|
final Game game = source.getGame();
|
||||||
|
|
||||||
if (sa.isSpell() && !source.isInPlay()) {
|
if (sa.isSpell() && source.isInPlay()) {
|
||||||
|
return alternatives;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sa.isSpell()) {
|
||||||
boolean lkicheck = false;
|
boolean lkicheck = false;
|
||||||
|
|
||||||
Card newHost = ((Spell)sa).getAlternateHost(source);
|
Card newHost = ((Spell)sa).getAlternateHost(source);
|
||||||
@@ -179,12 +178,13 @@ public final class GameActionUtil {
|
|||||||
|
|
||||||
SpellAbility newSA;
|
SpellAbility newSA;
|
||||||
if (source.getAlternateState().getType().hasSubtype("Aura")) {
|
if (source.getAlternateState().getType().hasSubtype("Aura")) {
|
||||||
newSA = source.getAlternateState().getFirstAbility().copyWithManaCostReplaced(activator,
|
newSA = source.getAlternateState().getFirstAbility().copyWithManaCostReplaced(activator, disturbCost);
|
||||||
disturbCost);
|
|
||||||
} else {
|
} else {
|
||||||
newSA = sa.copyWithManaCostReplaced(activator, disturbCost);
|
newSA = new SpellPermanent(source);
|
||||||
}
|
newSA.setCardState(source.getAlternateState());
|
||||||
|
newSA.setPayCosts(disturbCost);
|
||||||
newSA.setActivatingPlayer(activator);
|
newSA.setActivatingPlayer(activator);
|
||||||
|
}
|
||||||
|
|
||||||
newSA.putParam("PrecostDesc", "Disturb —");
|
newSA.putParam("PrecostDesc", "Disturb —");
|
||||||
newSA.putParam("CostDesc", disturbCost.toString());
|
newSA.putParam("CostDesc", disturbCost.toString());
|
||||||
@@ -210,7 +210,6 @@ public final class GameActionUtil {
|
|||||||
final Cost escapeCost = new Cost(k[1], true);
|
final Cost escapeCost = new Cost(k[1], true);
|
||||||
|
|
||||||
final SpellAbility newSA = sa.copyWithManaCostReplaced(activator, escapeCost);
|
final SpellAbility newSA = sa.copyWithManaCostReplaced(activator, escapeCost);
|
||||||
newSA.setActivatingPlayer(activator);
|
|
||||||
|
|
||||||
newSA.putParam("PrecostDesc", "Escape—");
|
newSA.putParam("PrecostDesc", "Escape—");
|
||||||
newSA.putParam("CostDesc", escapeCost.toString());
|
newSA.putParam("CostDesc", escapeCost.toString());
|
||||||
@@ -290,6 +289,35 @@ public final class GameActionUtil {
|
|||||||
foretold.putParam("AfterDescription", "(Foretold)");
|
foretold.putParam("AfterDescription", "(Foretold)");
|
||||||
alternatives.add(foretold);
|
alternatives.add(foretold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// some needs to check after ability was put on the stack
|
||||||
|
// Currently this is only checked for Toolbox and that only cares about creature spells
|
||||||
|
if (source.isCreature() && game.getAction().hasStaticAbilityAffectingZone(ZoneType.Stack, StaticAbilityLayer.ABILITIES)) {
|
||||||
|
Zone oldZone = source.getLastKnownZone();
|
||||||
|
Card blitzCopy = source;
|
||||||
|
if (!source.isLKI()) {
|
||||||
|
blitzCopy = CardUtil.getLKICopy(source);
|
||||||
|
}
|
||||||
|
blitzCopy.setLastKnownZone(game.getStackZone());
|
||||||
|
lkicheck = true;
|
||||||
|
|
||||||
|
blitzCopy.clearStaticChangedCardKeywords(false);
|
||||||
|
CardCollection preList = new CardCollection(blitzCopy);
|
||||||
|
game.getAction().checkStaticAbilities(false, Sets.newHashSet(blitzCopy), preList);
|
||||||
|
|
||||||
|
// currently only for Keyword BLitz, but should affect Dash probably too
|
||||||
|
for (final KeywordInterface inst : blitzCopy.getKeywords(Keyword.BLITZ)) {
|
||||||
|
// TODO with mana value 4 or greater has blitz.
|
||||||
|
for (SpellAbility iSa : inst.getAbilities()) {
|
||||||
|
// do only non intrinsic
|
||||||
|
if (!iSa.isIntrinsic()) {
|
||||||
|
alternatives.add(iSa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// need to reset to Old Zone, or canPlay would fail
|
||||||
|
blitzCopy.setLastKnownZone(oldZone);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset static abilities
|
// reset static abilities
|
||||||
@@ -300,8 +328,7 @@ public final class GameActionUtil {
|
|||||||
// need to unfreeze tracker
|
// need to unfreeze tracker
|
||||||
game.getTracker().unfreeze();
|
game.getTracker().unfreeze();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if (sa.isManaAbility() && sa.isActivatedAbility() && activator.hasKeyword("Piracy") && source.isLand() && source.isInPlay() && !activator.equals(source.getController()) && sa.getPayCosts().hasTapCost()) {
|
if (sa.isManaAbility() && sa.isActivatedAbility() && activator.hasKeyword("Piracy") && source.isLand() && source.isInPlay() && !activator.equals(source.getController()) && sa.getPayCosts().hasTapCost()) {
|
||||||
SpellAbility newSA = sa.copy(activator);
|
SpellAbility newSA = sa.copy(activator);
|
||||||
// to bypass Activator restriction, set Activator to Player
|
// to bypass Activator restriction, set Activator to Player
|
||||||
@@ -367,7 +394,7 @@ public final class GameActionUtil {
|
|||||||
alternatives.add(newSA);
|
alternatives.add(newSA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return alternatives;
|
return alternatives;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,13 +704,12 @@ public final class GameActionUtil {
|
|||||||
if (!StringUtils.isNumeric(amount)) {
|
if (!StringUtils.isNumeric(amount)) {
|
||||||
sa.setSVar(amount, sourceCard.getSVar(amount));
|
sa.setSVar(amount, sourceCard.getSVar(amount));
|
||||||
}
|
}
|
||||||
CardFactoryUtil.setupETBReplacementAbility(sa);
|
|
||||||
|
|
||||||
String desc = "It enters the battlefield with ";
|
String desc = "It enters the battlefield with ";
|
||||||
desc += Lang.nounWithNumeral(amount, CounterType.getType(counter).getName() + " counter");
|
desc += Lang.nounWithNumeral(amount, CounterType.getType(counter).getName() + " counter");
|
||||||
desc += " on it.";
|
desc += " on it.";
|
||||||
|
|
||||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | ReplacementResult$ Updated | Description$ " + desc;
|
||||||
|
|
||||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||||
re.setLayer(ReplacementLayer.Other);
|
re.setLayer(ReplacementLayer.Other);
|
||||||
|
|||||||
@@ -17,9 +17,13 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game;
|
package forge.game;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
@@ -46,6 +50,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
private String name = "";
|
private String name = "";
|
||||||
protected CardCollection attachedCards = new CardCollection();
|
protected CardCollection attachedCards = new CardCollection();
|
||||||
protected Map<CounterType, Integer> counters = Maps.newHashMap();
|
protected Map<CounterType, Integer> counters = Maps.newHashMap();
|
||||||
|
protected List<Pair<Integer, Boolean>> damageReceivedThisTurn = Lists.newArrayList();
|
||||||
|
|
||||||
protected GameEntity(int id0) {
|
protected GameEntity(int id0) {
|
||||||
id = id0;
|
id = id0;
|
||||||
@@ -330,6 +335,30 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
addCounterInternal(CounterType.get(counterType), n, source, fireEvents, table);
|
addCounterInternal(CounterType.get(counterType), n, source, fireEvents, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void receiveDamage(Pair<Integer, Boolean> dmg) {
|
||||||
|
damageReceivedThisTurn.add(dmg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getAssignedDamage() {
|
||||||
|
return getAssignedDamage(null, null);
|
||||||
|
}
|
||||||
|
public final int getAssignedCombatDamage() {
|
||||||
|
return getAssignedDamage(true, null);
|
||||||
|
}
|
||||||
|
public final int getAssignedDamage(Boolean isCombat, final Card source) {
|
||||||
|
int num = 0;
|
||||||
|
for (Pair<Integer, Boolean> dmg : damageReceivedThisTurn) {
|
||||||
|
if (isCombat != null && dmg.getRight() != isCombat) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (source != null && !getGame().getDamageLKI(dmg).getLeft().equalsWithTimestamp(source)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
num += dmg.getLeft();
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean equals(Object o) {
|
public final boolean equals(Object o) {
|
||||||
if (o == null) { return false; }
|
if (o == null) { return false; }
|
||||||
|
|||||||
@@ -94,10 +94,12 @@ public class GameEntityCounterTable extends ForwardingTable<Optional<Player>, Ga
|
|||||||
for (Map<CounterType, Integer> cm : gm.getValue().values()) {
|
for (Map<CounterType, Integer> cm : gm.getValue().values()) {
|
||||||
Integer old = ObjectUtils.firstNonNull(result.get(gm.getKey()), 0);
|
Integer old = ObjectUtils.firstNonNull(result.get(gm.getKey()), 0);
|
||||||
Integer v = ObjectUtils.firstNonNull(cm.get(type), 0);
|
Integer v = ObjectUtils.firstNonNull(cm.get(type), 0);
|
||||||
|
if (old + v > 0) {
|
||||||
result.put(gm.getKey(), old + v);
|
result.put(gm.getKey(), old + v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,8 +122,12 @@ public class GameEntityCounterTable extends ForwardingTable<Optional<Player>, Ga
|
|||||||
game.getTriggerHandler().runTrigger(TriggerType.CounterAddedAll, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.CounterAddedAll, runParams, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void replaceCounterEffect(final Game game, final SpellAbility cause, final boolean effect) {
|
public void replaceCounterEffect(final Game game, final SpellAbility cause, final boolean effect) {
|
||||||
|
replaceCounterEffect(game, cause, effect, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void replaceCounterEffect(final Game game, final SpellAbility cause, final boolean effect, final boolean etb, Map<AbilityKey, Object> params) {
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -133,6 +139,10 @@ public class GameEntityCounterTable extends ForwardingTable<Optional<Player>, Ga
|
|||||||
repParams.put(AbilityKey.Cause, cause);
|
repParams.put(AbilityKey.Cause, cause);
|
||||||
repParams.put(AbilityKey.EffectOnly, effect);
|
repParams.put(AbilityKey.EffectOnly, effect);
|
||||||
repParams.put(AbilityKey.CounterMap, values);
|
repParams.put(AbilityKey.CounterMap, values);
|
||||||
|
repParams.put(AbilityKey.ETB, etb);
|
||||||
|
if (params != null) {
|
||||||
|
repParams.putAll(params);
|
||||||
|
}
|
||||||
|
|
||||||
switch (game.getReplacementHandler().run(ReplacementType.AddCounter, repParams)) {
|
switch (game.getReplacementHandler().run(ReplacementType.AddCounter, repParams)) {
|
||||||
case NotReplaced:
|
case NotReplaced:
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
public enum FormatType {
|
public enum FormatType {
|
||||||
SANCTIONED,
|
SANCTIONED,
|
||||||
CASUAL,
|
CASUAL,
|
||||||
HISTORIC,
|
ARCHIVED,
|
||||||
DIGITAL,
|
DIGITAL,
|
||||||
CUSTOM
|
CUSTOM
|
||||||
}
|
}
|
||||||
@@ -290,7 +290,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
if (other.formatSubType != formatSubType){
|
if (other.formatSubType != formatSubType){
|
||||||
return formatSubType.compareTo(other.formatSubType);
|
return formatSubType.compareTo(other.formatSubType);
|
||||||
}
|
}
|
||||||
if (formatType.equals(FormatType.HISTORIC)){
|
if (formatType.equals(FormatType.ARCHIVED)){
|
||||||
int compareDates = this.effectiveDate.compareTo(other.effectiveDate);
|
int compareDates = this.effectiveDate.compareTo(other.effectiveDate);
|
||||||
if (compareDates != 0)
|
if (compareDates != 0)
|
||||||
return compareDates;
|
return compareDates;
|
||||||
@@ -306,7 +306,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
|
|
||||||
public static class Reader extends StorageReaderRecursiveFolderWithUserFolder<GameFormat> {
|
public static class Reader extends StorageReaderRecursiveFolderWithUserFolder<GameFormat> {
|
||||||
List<GameFormat> naturallyOrdered = new ArrayList<>();
|
List<GameFormat> naturallyOrdered = new ArrayList<>();
|
||||||
boolean includeHistoric;
|
boolean includeArchived;
|
||||||
private List<String> coreFormats = new ArrayList<>();
|
private List<String> coreFormats = new ArrayList<>();
|
||||||
{
|
{
|
||||||
coreFormats.add("Standard.txt");
|
coreFormats.add("Standard.txt");
|
||||||
@@ -321,14 +321,14 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
coreFormats.add("Oathbreaker.txt");
|
coreFormats.add("Oathbreaker.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Reader(File forgeFormats, File customFormats, boolean includeHistoric) {
|
public Reader(File forgeFormats, File customFormats, boolean includeArchived) {
|
||||||
super(forgeFormats, customFormats, GameFormat.FN_GET_NAME);
|
super(forgeFormats, customFormats, GameFormat.FN_GET_NAME);
|
||||||
this.includeHistoric=includeHistoric;
|
this.includeArchived=includeArchived;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected GameFormat read(File file) {
|
protected GameFormat read(File file) {
|
||||||
if (!includeHistoric && !coreFormats.contains(file.getName())) {
|
if (!includeArchived && !coreFormats.contains(file.getName())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
|
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
|
||||||
@@ -348,8 +348,13 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
try {
|
try {
|
||||||
formatType = FormatType.valueOf(section.get("type").toUpperCase());
|
formatType = FormatType.valueOf(section.get("type").toUpperCase());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if ("HISTORIC".equals(section.get("type").toUpperCase())) {
|
||||||
|
System.out.println("Historic is no longer used as a format Type. Please update " + file.getAbsolutePath() + " to use 'Archived' instead");
|
||||||
|
formatType = FormatType.ARCHIVED;
|
||||||
|
} else {
|
||||||
formatType = FormatType.CUSTOM;
|
formatType = FormatType.CUSTOM;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
FormatSubType formatsubType;
|
FormatSubType formatsubType;
|
||||||
try {
|
try {
|
||||||
formatsubType = FormatSubType.valueOf(section.get("subtype").toUpperCase());
|
formatsubType = FormatSubType.valueOf(section.get("subtype").toUpperCase());
|
||||||
@@ -450,7 +455,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
public Iterable<GameFormat> getFilterList() {
|
public Iterable<GameFormat> getFilterList() {
|
||||||
List<GameFormat> coreList = new ArrayList<>();
|
List<GameFormat> coreList = new ArrayList<>();
|
||||||
for (GameFormat format: naturallyOrdered) {
|
for (GameFormat format: naturallyOrdered) {
|
||||||
if (!format.getFormatType().equals(FormatType.HISTORIC)
|
if (!format.getFormatType().equals(FormatType.ARCHIVED)
|
||||||
&&!format.getFormatType().equals(FormatType.DIGITAL)){
|
&&!format.getFormatType().equals(FormatType.DIGITAL)){
|
||||||
coreList.add(format);
|
coreList.add(format);
|
||||||
}
|
}
|
||||||
@@ -458,10 +463,10 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
return coreList;
|
return coreList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<GameFormat> getHistoricList() {
|
public Iterable<GameFormat> getArchivedList() {
|
||||||
List<GameFormat> coreList = new ArrayList<>();
|
List<GameFormat> coreList = new ArrayList<>();
|
||||||
for (GameFormat format: naturallyOrdered) {
|
for (GameFormat format: naturallyOrdered) {
|
||||||
if (format.getFormatType().equals(FormatType.HISTORIC)){
|
if (format.getFormatType().equals(FormatType.ARCHIVED)){
|
||||||
coreList.add(format);
|
coreList.add(format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -470,7 +475,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
|
|
||||||
public Iterable<GameFormat> getBlockList() {
|
public Iterable<GameFormat> getBlockList() {
|
||||||
List<GameFormat> blockFormats = new ArrayList<>();
|
List<GameFormat> blockFormats = new ArrayList<>();
|
||||||
for (GameFormat format : this.getHistoricList()){
|
for (GameFormat format : this.getArchivedList()){
|
||||||
if (format.getFormatSubType() != GameFormat.FormatSubType.BLOCK)
|
if (format.getFormatSubType() != GameFormat.FormatSubType.BLOCK)
|
||||||
continue;
|
continue;
|
||||||
if (!format.getName().endsWith("Block"))
|
if (!format.getName().endsWith("Block"))
|
||||||
@@ -481,10 +486,10 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
return blockFormats;
|
return blockFormats;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, List<GameFormat>> getHistoricMap() {
|
public Map<String, List<GameFormat>> getArchivedMap() {
|
||||||
Map<String, List<GameFormat>> coreList = new HashMap<>();
|
Map<String, List<GameFormat>> coreList = new HashMap<>();
|
||||||
for (GameFormat format: naturallyOrdered){
|
for (GameFormat format: naturallyOrdered){
|
||||||
if (format.getFormatType().equals(FormatType.HISTORIC)){
|
if (format.getFormatType().equals(FormatType.ARCHIVED)){
|
||||||
String alpha = format.getName().substring(0,1);
|
String alpha = format.getName().substring(0,1);
|
||||||
if (!coreList.containsKey(alpha)) {
|
if (!coreList.containsKey(alpha)) {
|
||||||
coreList.put(alpha,new ArrayList<>());
|
coreList.put(alpha,new ArrayList<>());
|
||||||
@@ -557,9 +562,9 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
//exclude Commander format as other deck checks are not performed here
|
//exclude Commander format as other deck checks are not performed here
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (gf.getFormatType().equals(FormatType.HISTORIC) && coveredTypes.contains(gf.getFormatSubType())
|
if (gf.getFormatType().equals(FormatType.ARCHIVED) && coveredTypes.contains(gf.getFormatSubType())
|
||||||
&& !exhaustive){
|
&& !exhaustive){
|
||||||
//exclude duplicate formats - only keep first of e.g. Standard historical
|
//exclude duplicate formats - only keep first of e.g. Standard archived
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (gf.isPoolLegal(allCards)) {
|
if (gf.isPoolLegal(allCards)) {
|
||||||
@@ -590,7 +595,7 @@ public class GameFormat implements Comparable<GameFormat> {
|
|||||||
if (gf2.formatSubType != gf1.formatSubType){
|
if (gf2.formatSubType != gf1.formatSubType){
|
||||||
return gf1.formatSubType.compareTo(gf2.formatSubType);
|
return gf1.formatSubType.compareTo(gf2.formatSubType);
|
||||||
}
|
}
|
||||||
if (gf1.formatType.equals(FormatType.HISTORIC)){
|
if (gf1.formatType.equals(FormatType.ARCHIVED)){
|
||||||
if (gf1.effectiveDate!=gf2.effectiveDate) {//for matching dates or default dates default to name sorting
|
if (gf1.effectiveDate!=gf2.effectiveDate) {//for matching dates or default dates default to name sorting
|
||||||
return gf1.effectiveDate.compareTo(gf2.effectiveDate);
|
return gf1.effectiveDate.compareTo(gf2.effectiveDate);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ public class GameRules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setAppliedVariants(final Set<GameType> appliedVariants) {
|
public void setAppliedVariants(final Set<GameType> appliedVariants) {
|
||||||
|
if (appliedVariants != null && !appliedVariants.isEmpty())
|
||||||
this.appliedVariants.addAll(appliedVariants);
|
this.appliedVariants.addAll(appliedVariants);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public enum GameType {
|
|||||||
|
|
||||||
private final DeckFormat deckFormat;
|
private final DeckFormat deckFormat;
|
||||||
private final boolean isCardPoolLimited, canSideboard, addWonCardsMidGame;
|
private final boolean isCardPoolLimited, canSideboard, addWonCardsMidGame;
|
||||||
private final String name, description;
|
private final String name, englishName, description;
|
||||||
private final Function<RegisteredPlayer, Deck> deckAutoGenerator;
|
private final Function<RegisteredPlayer, Deck> deckAutoGenerator;
|
||||||
|
|
||||||
GameType(DeckFormat deckFormat0, boolean isCardPoolLimited0, boolean canSideboard0, boolean addWonCardsMidgame0, String name0, String description0) {
|
GameType(DeckFormat deckFormat0, boolean isCardPoolLimited0, boolean canSideboard0, boolean addWonCardsMidgame0, String name0, String description0) {
|
||||||
@@ -90,6 +90,7 @@ public enum GameType {
|
|||||||
canSideboard = canSideboard0;
|
canSideboard = canSideboard0;
|
||||||
addWonCardsMidGame = addWonCardsMidgame0;
|
addWonCardsMidGame = addWonCardsMidgame0;
|
||||||
name = localizer.getMessage(name0);
|
name = localizer.getMessage(name0);
|
||||||
|
englishName = localizer.getEnglishMessage(name0);
|
||||||
if (description0.length()>0) {
|
if (description0.length()>0) {
|
||||||
description0 = localizer.getMessage(description0);
|
description0 = localizer.getMessage(description0);
|
||||||
}
|
}
|
||||||
@@ -148,6 +149,9 @@ public enum GameType {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
public String getEnglishName() {
|
||||||
|
return englishName;
|
||||||
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ public enum GlobalRuleChange {
|
|||||||
onlyOneBlocker ("No more than one creature can block each combat."),
|
onlyOneBlocker ("No more than one creature can block each combat."),
|
||||||
onlyOneBlockerPerOpponent ("Each opponent can't block with more than one creature."),
|
onlyOneBlockerPerOpponent ("Each opponent can't block with more than one creature."),
|
||||||
onlyTwoBlockers ("No more than two creatures can block each combat."),
|
onlyTwoBlockers ("No more than two creatures can block each combat."),
|
||||||
toughnessAssignsDamage ("Each creature assigns combat damage equal to its toughness rather than its power."),
|
|
||||||
blankIsChaos("Each blank roll of the planar dice is a {CHAOS} roll.");
|
blankIsChaos("Each blank roll of the planar dice is a {CHAOS} roll.");
|
||||||
|
|
||||||
private final String ruleText;
|
private final String ruleText;
|
||||||
|
|||||||
@@ -288,6 +288,8 @@ public class StaticEffect {
|
|||||||
affectedCard.removeCanBlockAdditional(getTimestamp());
|
affectedCard.removeCanBlockAdditional(getTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
affectedCard.removeChangedSVars(getTimestamp(), ability.getId());
|
||||||
|
|
||||||
affectedCard.updateAbilityTextForView(); // only update keywords and text for view to avoid flickering
|
affectedCard.updateAbilityTextForView(); // only update keywords and text for view to avoid flickering
|
||||||
}
|
}
|
||||||
return affectedCards;
|
return affectedCards;
|
||||||
|
|||||||
@@ -327,8 +327,8 @@ public final class AbilityFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final TargetRestrictions readTarget(Map<String, String> mapParams) {
|
private static final TargetRestrictions readTarget(Map<String, String> mapParams) {
|
||||||
final String min = mapParams.containsKey("TargetMin") ? mapParams.get("TargetMin") : "1";
|
final String min = mapParams.getOrDefault("TargetMin", "1");
|
||||||
final String max = mapParams.containsKey("TargetMax") ? mapParams.get("TargetMax") : "1";
|
final String max = mapParams.getOrDefault("TargetMax", "1");
|
||||||
|
|
||||||
// TgtPrompt should only be needed for more complicated ValidTgts
|
// TgtPrompt should only be needed for more complicated ValidTgts
|
||||||
String tgtWhat = mapParams.get("ValidTgts");
|
String tgtWhat = mapParams.get("ValidTgts");
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ public enum AbilityKey {
|
|||||||
Explorer("Explorer"),
|
Explorer("Explorer"),
|
||||||
ExtraTurn("ExtraTurn"),
|
ExtraTurn("ExtraTurn"),
|
||||||
Event("Event"),
|
Event("Event"),
|
||||||
|
ETB("ETB"),
|
||||||
Fighter("Fighter"),
|
Fighter("Fighter"),
|
||||||
Fighters("Fighters"),
|
Fighters("Fighters"),
|
||||||
FirstTime("FirstTime"),
|
FirstTime("FirstTime"),
|
||||||
@@ -112,12 +113,14 @@ public enum AbilityKey {
|
|||||||
Result("Result"),
|
Result("Result"),
|
||||||
RoomName("RoomName"),
|
RoomName("RoomName"),
|
||||||
Scheme("Scheme"),
|
Scheme("Scheme"),
|
||||||
|
ScryNum("ScryNum"),
|
||||||
Sides("Sides"),
|
Sides("Sides"),
|
||||||
Source("Source"),
|
Source("Source"),
|
||||||
Sources("Sources"),
|
Sources("Sources"),
|
||||||
SourceSA("SourceSA"),
|
SourceSA("SourceSA"),
|
||||||
SpellAbility("SpellAbility"),
|
SpellAbility("SpellAbility"),
|
||||||
SpellAbilityStackInstance("SpellAbilityStackInstance"),
|
SpellAbilityStackInstance("SpellAbilityStackInstance"),
|
||||||
|
SpellAbilityTarget("SpellAbilityTarget"),
|
||||||
SpellAbilityTargetingCards("SpellAbilityTargetingCards"),
|
SpellAbilityTargetingCards("SpellAbilityTargetingCards"),
|
||||||
StackInstance("StackInstance"),
|
StackInstance("StackInstance"),
|
||||||
StackSa("StackSa"),
|
StackSa("StackSa"),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@@ -146,7 +147,7 @@ public class AbilityUtils {
|
|||||||
} else if (defined.equals("TopOfGraveyard")) {
|
} else if (defined.equals("TopOfGraveyard")) {
|
||||||
final CardCollectionView grave = hostCard.getController().getCardsIn(ZoneType.Graveyard);
|
final CardCollectionView grave = hostCard.getController().getCardsIn(ZoneType.Graveyard);
|
||||||
|
|
||||||
if (grave.size() > 0) { // TopOfLibrary or BottomOfLibrary
|
if (grave.size() > 0) {
|
||||||
c = grave.getLast();
|
c = grave.getLast();
|
||||||
} else {
|
} else {
|
||||||
// we don't want this to fall through and return the "Self"
|
// we don't want this to fall through and return the "Self"
|
||||||
@@ -220,7 +221,7 @@ public class AbilityUtils {
|
|||||||
final Object crd = root.getReplacingObject(type);
|
final Object crd = root.getReplacingObject(type);
|
||||||
|
|
||||||
if (crd instanceof Card) {
|
if (crd instanceof Card) {
|
||||||
c = game.getCardState((Card) crd);
|
c = (Card) crd;
|
||||||
} else if (crd instanceof Iterable<?>) {
|
} else if (crd instanceof Iterable<?>) {
|
||||||
cards.addAll(Iterables.filter((Iterable<?>) crd, Card.class));
|
cards.addAll(Iterables.filter((Iterable<?>) crd, Card.class));
|
||||||
}
|
}
|
||||||
@@ -535,6 +536,9 @@ public class AbilityUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val = playerXCount(players, calcX[1], card, ability);
|
val = playerXCount(players, calcX[1], card, ability);
|
||||||
|
} else if (hType.startsWith("Defined")) {
|
||||||
|
String defined = hType.split("Defined")[1];
|
||||||
|
val = playerXCount(getDefinedPlayers(card, defined, ability), calcX[1], card, ability);
|
||||||
} else {
|
} else {
|
||||||
val = 0;
|
val = 0;
|
||||||
}
|
}
|
||||||
@@ -1060,17 +1064,16 @@ public class AbilityUtils {
|
|||||||
final SpellAbility root = ((SpellAbility)sa).getRootAbility();
|
final SpellAbility root = ((SpellAbility)sa).getRootAbility();
|
||||||
Object o = null;
|
Object o = null;
|
||||||
if (defParsed.endsWith("Controller")) {
|
if (defParsed.endsWith("Controller")) {
|
||||||
String triggeringType = defParsed.substring(9);
|
final boolean orCont = defParsed.endsWith("OrController");
|
||||||
triggeringType = triggeringType.substring(0, triggeringType.length() - 10);
|
String triggeringType = defParsed.substring(9, defParsed.length() - (orCont ? 12 : 10));
|
||||||
final Object c = root.getTriggeringObject(AbilityKey.fromString(triggeringType));
|
final Object c = root.getTriggeringObject(AbilityKey.fromString(triggeringType));
|
||||||
if (c instanceof Card) {
|
if (orCont && c instanceof Player) {
|
||||||
|
o = c;
|
||||||
|
} else if (c instanceof Card) {
|
||||||
o = ((Card) c).getController();
|
o = ((Card) c).getController();
|
||||||
}
|
} else if (c instanceof SpellAbility) {
|
||||||
if (c instanceof SpellAbility) {
|
|
||||||
o = ((SpellAbility) c).getActivatingPlayer();
|
o = ((SpellAbility) c).getActivatingPlayer();
|
||||||
}
|
} else if (c instanceof CardCollection) { // For merged permanent
|
||||||
// For merged permanent
|
|
||||||
if (c instanceof CardCollection) {
|
|
||||||
o = ((CardCollection) c).get(0).getController();
|
o = ((CardCollection) c).get(0).getController();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1413,11 +1416,7 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
// Needed - Equip an untapped creature with Sword of the Paruns then cast Deadshot on it. Should deal 2 more damage.
|
// Needed - Equip an untapped creature with Sword of the Paruns then cast Deadshot on it. Should deal 2 more damage.
|
||||||
game.getAction().checkStaticAbilities(); // this will refresh continuous abilities for players and permanents.
|
game.getAction().checkStaticAbilities(); // this will refresh continuous abilities for players and permanents.
|
||||||
if (sa.isReplacementAbility() && abSub.getApi() == ApiType.InternalEtbReplacement) {
|
game.getTriggerHandler().resetActiveTriggers(!sa.isReplacementAbility());
|
||||||
game.getTriggerHandler().resetActiveTriggers(false);
|
|
||||||
} else {
|
|
||||||
game.getTriggerHandler().resetActiveTriggers();
|
|
||||||
}
|
|
||||||
AbilityUtils.resolveApiAbility(abSub, game);
|
AbilityUtils.resolveApiAbility(abSub, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1448,7 +1447,7 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
// The player who has the chance to cancel the ability
|
// The player who has the chance to cancel the ability
|
||||||
final String pays = sa.getParamOrDefault("UnlessPayer", "TargetedController");
|
final String pays = sa.getParamOrDefault("UnlessPayer", "TargetedController");
|
||||||
final FCollectionView<Player> allPayers = getDefinedPlayers(sa.getHostCard(), pays, sa);
|
final FCollectionView<Player> allPayers = getDefinedPlayers(source, pays, sa);
|
||||||
final String resolveSubs = sa.getParam("UnlessResolveSubs"); // no value means 'Always'
|
final String resolveSubs = sa.getParam("UnlessResolveSubs"); // no value means 'Always'
|
||||||
final boolean execSubsWhenPaid = "WhenPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs);
|
final boolean execSubsWhenPaid = "WhenPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs);
|
||||||
final boolean execSubsWhenNotPaid = "WhenNotPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs);
|
final boolean execSubsWhenNotPaid = "WhenNotPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs);
|
||||||
@@ -1485,7 +1484,7 @@ public class AbilityUtils {
|
|||||||
cost = new Cost(new ManaCost(new ManaCostParser(String.valueOf(source.getChosenNumber()))), true);
|
cost = new Cost(new ManaCost(new ManaCostParser(String.valueOf(source.getChosenNumber()))), true);
|
||||||
}
|
}
|
||||||
else if (unlessCost.startsWith("DefinedCost")) {
|
else if (unlessCost.startsWith("DefinedCost")) {
|
||||||
CardCollection definedCards = getDefinedCards(sa.getHostCard(), unlessCost.split("_")[1], sa);
|
CardCollection definedCards = getDefinedCards(source, unlessCost.split("_")[1], sa);
|
||||||
if (definedCards.isEmpty()) {
|
if (definedCards.isEmpty()) {
|
||||||
sa.resolve();
|
sa.resolve();
|
||||||
resolveSubAbilities(sa, game);
|
resolveSubAbilities(sa, game);
|
||||||
@@ -1505,7 +1504,7 @@ public class AbilityUtils {
|
|||||||
cost = new Cost(newCost.toManaCost(), true);
|
cost = new Cost(newCost.toManaCost(), true);
|
||||||
}
|
}
|
||||||
else if (unlessCost.startsWith("DefinedSACost")) {
|
else if (unlessCost.startsWith("DefinedSACost")) {
|
||||||
FCollection<SpellAbility> definedSAs = getDefinedSpellAbilities(sa.getHostCard(), unlessCost.split("_")[1], sa);
|
FCollection<SpellAbility> definedSAs = getDefinedSpellAbilities(source, unlessCost.split("_")[1], sa);
|
||||||
if (definedSAs.isEmpty()) {
|
if (definedSAs.isEmpty()) {
|
||||||
sa.resolve();
|
sa.resolve();
|
||||||
resolveSubAbilities(sa, game);
|
resolveSubAbilities(sa, game);
|
||||||
@@ -2032,7 +2031,7 @@ public class AbilityUtils {
|
|||||||
return doXMath(c.getTotalDamageDoneBy(), expr, c, ctb);
|
return doXMath(c.getTotalDamageDoneBy(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
if (sq[0].equals("TotalDamageReceivedThisTurn")) {
|
if (sq[0].equals("TotalDamageReceivedThisTurn")) {
|
||||||
return doXMath(c.getTotalDamageReceivedThisTurn(), expr, c, ctb);
|
return doXMath(c.getAssignedDamage(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sq[0].contains("CardPower")) {
|
if (sq[0].contains("CardPower")) {
|
||||||
@@ -2093,13 +2092,6 @@ public class AbilityUtils {
|
|||||||
return doXMath(c.getTimesMutated(), expr, c, ctb);
|
return doXMath(c.getTimesMutated(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sq[0].startsWith("DamageDoneByPlayerThisTurn")) {
|
|
||||||
int sum = 0;
|
|
||||||
for (Player p : getDefinedPlayers(c, sq[1], ctb)) {
|
|
||||||
sum += c.getReceivedDamageByPlayerThisTurn(p);
|
|
||||||
}
|
|
||||||
return doXMath(sum, expr, c, ctb);
|
|
||||||
}
|
|
||||||
if (sq[0].equals("RegeneratedThisTurn")) {
|
if (sq[0].equals("RegeneratedThisTurn")) {
|
||||||
return doXMath(c.getRegeneratedThisTurn(), expr, c, ctb);
|
return doXMath(c.getRegeneratedThisTurn(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
@@ -2260,6 +2252,9 @@ public class AbilityUtils {
|
|||||||
if (sq[0].equals("Monarch")) {
|
if (sq[0].equals("Monarch")) {
|
||||||
return doXMath(calculateAmount(c, sq[player.isMonarch() ? 1 : 2], ctb), expr, c, ctb);
|
return doXMath(calculateAmount(c, sq[player.isMonarch() ? 1 : 2], ctb), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
if (sq[0].equals("Initiative")) {
|
||||||
|
return doXMath(calculateAmount(c, sq[player.hasInitiative() ? 1: 2], ctb), expr, c, ctb);
|
||||||
|
}
|
||||||
if (sq[0].equals("StartingPlayer")) {
|
if (sq[0].equals("StartingPlayer")) {
|
||||||
return doXMath(calculateAmount(c, sq[player.isStartingPlayer() ? 1: 2], ctb), expr, c, ctb);
|
return doXMath(calculateAmount(c, sq[player.isStartingPlayer() ? 1: 2], ctb), expr, c, ctb);
|
||||||
}
|
}
|
||||||
@@ -2358,20 +2353,28 @@ public class AbilityUtils {
|
|||||||
return doXMath(player.getOpponentsTotalPoisonCounters(), expr, c, ctb);
|
return doXMath(player.getOpponentsTotalPoisonCounters(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sq[0].equals("YourDamageThisTurn")) {
|
|
||||||
return doXMath(player.getAssignedDamage(), expr, c, ctb);
|
|
||||||
}
|
|
||||||
if (sq[0].equals("TotalOppDamageThisTurn")) {
|
|
||||||
return doXMath(player.getOpponentsAssignedDamage(), expr, c, ctb);
|
|
||||||
}
|
|
||||||
if (sq[0].equals("MaxOppDamageThisTurn")) {
|
if (sq[0].equals("MaxOppDamageThisTurn")) {
|
||||||
return doXMath(player.getMaxOpponentAssignedDamage(), expr, c, ctb);
|
return doXMath(player.getMaxOpponentAssignedDamage(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sq[0].startsWith("YourDamageSourcesThisTurn")) {
|
if (sq[0].contains("TotalDamageThisTurn")) {
|
||||||
Iterable<Card> allSrc = player.getAssignedDamageSources();
|
String[] props = l[0].split(" ");
|
||||||
String restriction = sq[0].split(" ")[1];
|
int sum = 0;
|
||||||
return doXMath(CardLists.getValidCardCount(allSrc, restriction, player, c, ctb), expr, c, ctb);
|
for (Pair<Integer, Boolean> p : c.getDamageReceivedThisTurn()) {
|
||||||
|
if (game.getDamageLKI(p).getLeft().isValid(props[1], player, c, ctb)) {
|
||||||
|
sum += p.getLeft();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return doXMath(sum, expr, c, ctb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sq[0].contains("DamageThisTurn")) {
|
||||||
|
String[] props = l[0].split(" ");
|
||||||
|
Boolean isCombat = null;
|
||||||
|
if (sq[0].contains("CombatDamage")) {
|
||||||
|
isCombat = true;
|
||||||
|
}
|
||||||
|
return doXMath(game.getDamageDoneThisTurn(isCombat, false, props[1], props[2], c, player, ctb).size(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sq[0].equals("YourTurns")) {
|
if (sq[0].equals("YourTurns")) {
|
||||||
@@ -3324,12 +3327,6 @@ public class AbilityUtils {
|
|||||||
totDmg += p.getAssignedDamage();
|
totDmg += p.getAssignedDamage();
|
||||||
}
|
}
|
||||||
return doXMath(totDmg, m, source, ctb);
|
return doXMath(totDmg, m, source, ctb);
|
||||||
} else if (sq[0].contains("LifeLostThisTurn")) {
|
|
||||||
int totDmg = 0;
|
|
||||||
for (Player p : players) {
|
|
||||||
totDmg += p.getLifeLostThisTurn();
|
|
||||||
}
|
|
||||||
return doXMath(totDmg, m, source, ctb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (players.size() > 0) {
|
if (players.size() > 0) {
|
||||||
@@ -3386,7 +3383,7 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
if (value.contains("DomainPlayer")) {
|
if (value.contains("DomainPlayer")) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
final CardCollectionView someCards = player.getCardsIn(ZoneType.Battlefield);
|
final CardCollectionView someCards = player.getLandsInPlay();
|
||||||
final List<String> basic = MagicColor.Constant.BASIC_LANDS;
|
final List<String> basic = MagicColor.Constant.BASIC_LANDS;
|
||||||
|
|
||||||
for (int i = 0; i < basic.size(); i++) {
|
for (int i = 0; i < basic.size(); i++) {
|
||||||
@@ -3483,6 +3480,10 @@ public class AbilityUtils {
|
|||||||
return doXMath(opps == null ? 0 : opps.size(), m, source, ctb);
|
return doXMath(opps == null ? 0 : opps.size(), m, source, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.equals("OpponentsAttackedThisCombat")) {
|
||||||
|
return doXMath(game.getCombat().getAttackedOpponents(player).size(), m, source, ctb);
|
||||||
|
}
|
||||||
|
|
||||||
if (value.equals("DungeonsCompleted")) {
|
if (value.equals("DungeonsCompleted")) {
|
||||||
return doXMath(player.getCompletedDungeons().size(), m, source, ctb);
|
return doXMath(player.getCompletedDungeons().size(), m, source, ctb);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ public enum ApiType {
|
|||||||
DigUntil (DigUntilEffect.class),
|
DigUntil (DigUntilEffect.class),
|
||||||
Discard (DiscardEffect.class),
|
Discard (DiscardEffect.class),
|
||||||
DrainMana (DrainManaEffect.class),
|
DrainMana (DrainManaEffect.class),
|
||||||
|
Draft (DraftEffect.class),
|
||||||
Draw (DrawEffect.class),
|
Draw (DrawEffect.class),
|
||||||
EachDamage (DamageEachEffect.class),
|
EachDamage (DamageEachEffect.class),
|
||||||
Effect (EffectEffect.class),
|
Effect (EffectEffect.class),
|
||||||
@@ -171,6 +172,7 @@ public enum ApiType {
|
|||||||
Subgame (SubgameEffect.class),
|
Subgame (SubgameEffect.class),
|
||||||
Surveil (SurveilEffect.class),
|
Surveil (SurveilEffect.class),
|
||||||
SwitchBlock (SwitchBlockEffect.class),
|
SwitchBlock (SwitchBlockEffect.class),
|
||||||
|
TakeInitiative (TakeInitiativeEffect.class),
|
||||||
Tap (TapEffect.class),
|
Tap (TapEffect.class),
|
||||||
TapAll (TapAllEffect.class),
|
TapAll (TapAllEffect.class),
|
||||||
TapOrUntap (TapOrUntapEffect.class),
|
TapOrUntap (TapOrUntapEffect.class),
|
||||||
@@ -188,7 +190,6 @@ public enum ApiType {
|
|||||||
|
|
||||||
DamageResolve (DamageResolveEffect.class),
|
DamageResolve (DamageResolveEffect.class),
|
||||||
ChangeZoneResolve (ChangeZoneResolveEffect.class),
|
ChangeZoneResolve (ChangeZoneResolveEffect.class),
|
||||||
InternalEtbReplacement (ETBReplacementEffect.class),
|
|
||||||
InternalLegendaryRule (CharmEffect.class),
|
InternalLegendaryRule (CharmEffect.class),
|
||||||
InternalIgnoreEffect (CharmEffect.class),
|
InternalIgnoreEffect (CharmEffect.class),
|
||||||
UpdateRemember (UpdateRememberEffect.class);
|
UpdateRemember (UpdateRememberEffect.class);
|
||||||
|
|||||||
@@ -300,13 +300,12 @@ public abstract class SpellAbilityEffect {
|
|||||||
delTrig.append("| TriggerDescription$ ").append(desc);
|
delTrig.append("| TriggerDescription$ ").append(desc);
|
||||||
|
|
||||||
final Trigger trig = TriggerHandler.parseTrigger(delTrig.toString(), CardUtil.getLKICopy(sa.getHostCard()), intrinsic);
|
final Trigger trig = TriggerHandler.parseTrigger(delTrig.toString(), CardUtil.getLKICopy(sa.getHostCard()), intrinsic);
|
||||||
|
long ts = sa.getHostCard().getGame().getNextTimestamp();
|
||||||
for (final Card c : crds) {
|
for (final Card c : crds) {
|
||||||
trig.addRemembered(c);
|
trig.addRemembered(c);
|
||||||
|
|
||||||
// Svar for AI
|
// Svar for AI
|
||||||
if (!c.hasSVar("EndOfTurnLeavePlay")) {
|
c.addChangedSVars(Collections.singletonMap("EndOfTurnLeavePlay", "AtEOT"), ts, 0);
|
||||||
c.setSVar("EndOfTurnLeavePlay", "AtEOT");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
String trigSA = "";
|
String trigSA = "";
|
||||||
if (location.equals("Hand")) {
|
if (location.equals("Hand")) {
|
||||||
@@ -346,9 +345,7 @@ public abstract class SpellAbilityEffect {
|
|||||||
card.addTrigger(trig);
|
card.addTrigger(trig);
|
||||||
|
|
||||||
// Svar for AI
|
// Svar for AI
|
||||||
if (!card.hasSVar("EndOfTurnLeavePlay")) {
|
card.addChangedSVars(Collections.singletonMap("EndOfTurnLeavePlay", "AtEOT"), card.getGame().getNextTimestamp(), 0);
|
||||||
card.setSVar("EndOfTurnLeavePlay", "AtEOT");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static SpellAbility getForgetSpellAbility(final Card card) {
|
protected static SpellAbility getForgetSpellAbility(final Card card) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class AbandonEffect extends SpellAbilityEffect {
|
|||||||
Player controller = source.getController();
|
Player controller = source.getController();
|
||||||
|
|
||||||
boolean isOptional = sa.hasParam("Optional");
|
boolean isOptional = sa.hasParam("Optional");
|
||||||
if (isOptional && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblWouldYouLikeAbandonSource", CardTranslation.getTranslatedName(source.getName())))) {
|
if (isOptional && !controller.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblWouldYouLikeAbandonSource", CardTranslation.getTranslatedName(source.getName())), null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.GameCommand;
|
import forge.GameCommand;
|
||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
@@ -64,17 +65,17 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
types.add(host.getChosenType2());
|
types.add(host.getChosenType2());
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<String> keywords = new ArrayList<>();
|
final List<String> keywords = Lists.newArrayList();
|
||||||
if (sa.hasParam("Keywords")) {
|
if (sa.hasParam("Keywords")) {
|
||||||
keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & ")));
|
keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & ")));
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<String> removeKeywords = new ArrayList<>();
|
final List<String> removeKeywords = Lists.newArrayList();
|
||||||
if (sa.hasParam("RemoveKeywords")) {
|
if (sa.hasParam("RemoveKeywords")) {
|
||||||
removeKeywords.addAll(Arrays.asList(sa.getParam("RemoveKeywords").split(" & ")));
|
removeKeywords.addAll(Arrays.asList(sa.getParam("RemoveKeywords").split(" & ")));
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<String> hiddenKeywords = new ArrayList<>();
|
final List<String> hiddenKeywords = Lists.newArrayList();
|
||||||
if (sa.hasParam("HiddenKeywords")) {
|
if (sa.hasParam("HiddenKeywords")) {
|
||||||
hiddenKeywords.addAll(Arrays.asList(sa.getParam("HiddenKeywords").split(" & ")));
|
hiddenKeywords.addAll(Arrays.asList(sa.getParam("HiddenKeywords").split(" & ")));
|
||||||
}
|
}
|
||||||
@@ -99,27 +100,38 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// abilities to add to the animated being
|
// abilities to add to the animated being
|
||||||
final List<String> abilities = new ArrayList<>();
|
final List<String> abilities = Lists.newArrayList();
|
||||||
if (sa.hasParam("Abilities")) {
|
if (sa.hasParam("Abilities")) {
|
||||||
abilities.addAll(Arrays.asList(sa.getParam("Abilities").split(",")));
|
abilities.addAll(Arrays.asList(sa.getParam("Abilities").split(",")));
|
||||||
}
|
}
|
||||||
// replacement effects to add to the animated being
|
// replacement effects to add to the animated being
|
||||||
final List<String> replacements = new ArrayList<>();
|
final List<String> replacements = Lists.newArrayList();
|
||||||
if (sa.hasParam("Replacements")) {
|
if (sa.hasParam("Replacements")) {
|
||||||
replacements.addAll(Arrays.asList(sa.getParam("Replacements").split(",")));
|
replacements.addAll(Arrays.asList(sa.getParam("Replacements").split(",")));
|
||||||
}
|
}
|
||||||
// triggers to add to the animated being
|
// triggers to add to the animated being
|
||||||
final List<String> triggers = new ArrayList<>();
|
final List<String> triggers = Lists.newArrayList();
|
||||||
if (sa.hasParam("Triggers")) {
|
if (sa.hasParam("Triggers")) {
|
||||||
triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(",")));
|
triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// sVars to add to the animated being
|
// sVars to add to the animated being
|
||||||
final List<String> sVars = new ArrayList<>();
|
final List<String> sVars = Lists.newArrayList();
|
||||||
if (sa.hasParam("sVars")) {
|
if (sa.hasParam("sVars")) {
|
||||||
sVars.addAll(Arrays.asList(sa.getParam("sVars").split(",")));
|
sVars.addAll(Arrays.asList(sa.getParam("sVars").split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static abilities to add to the animated being
|
||||||
|
final List<String> stAbs = Lists.newArrayList();
|
||||||
|
if (sa.hasParam("staticAbilities")) {
|
||||||
|
stAbs.addAll(Arrays.asList(sa.getParam("staticAbilities").split(",")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> sVarsMap = Maps.newHashMap();
|
||||||
|
for (final String s : sVars) {
|
||||||
|
sVarsMap.put(s, AbilityUtils.getSVar(sa, s));
|
||||||
|
}
|
||||||
|
|
||||||
final String valid = sa.getParamOrDefault("ValidCards", "");
|
final String valid = sa.getParamOrDefault("ValidCards", "");
|
||||||
|
|
||||||
CardCollectionView list;
|
CardCollectionView list;
|
||||||
@@ -133,15 +145,14 @@ public class AnimateAllEffect extends AnimateEffectBase {
|
|||||||
list = CardLists.getValidCards(list, valid, host.getController(), host, sa);
|
list = CardLists.getValidCards(list, valid, host.getController(), host, sa);
|
||||||
|
|
||||||
for (final Card c : list) {
|
for (final Card c : list) {
|
||||||
doAnimate(c, sa, power, toughness, types, removeTypes, finalColors,
|
doAnimate(c, sa, power, toughness, types, removeTypes, finalColors, keywords, removeKeywords,
|
||||||
keywords, removeKeywords, hiddenKeywords,
|
hiddenKeywords, abilities, triggers, replacements, stAbs, timestamp);
|
||||||
abilities, triggers, replacements, ImmutableList.of(),
|
|
||||||
timestamp);
|
|
||||||
|
|
||||||
// give sVars
|
// give sVars
|
||||||
for (final String s : sVars) {
|
if (!sVarsMap.isEmpty() ) {
|
||||||
c.setSVar(s, AbilityUtils.getSVar(sa, s));
|
c.addChangedSVars(sVarsMap, timestamp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
game.fireEvent(new GameEventCardStatsChanged(c));
|
game.fireEvent(new GameEventCardStatsChanged(c));
|
||||||
|
|
||||||
final GameCommand unanimate = new GameCommand() {
|
final GameCommand unanimate = new GameCommand() {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package forge.game.ability.effects;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
@@ -138,10 +140,21 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sVars to add to the animated being
|
// sVars to add to the animated being
|
||||||
final List<String> sVars = Lists.newArrayList();
|
Map<String, String> sVarsMap = Maps.newHashMap();
|
||||||
if (sa.hasParam("sVars")) {
|
if (sa.hasParam("sVars")) {
|
||||||
sVars.addAll(Arrays.asList(sa.getParam("sVars").split(",")));
|
for (final String s : sa.getParam("sVars").split(",")) {
|
||||||
|
String actualsVar = AbilityUtils.getSVar(sa, s);
|
||||||
|
String name = s;
|
||||||
|
if (actualsVar.startsWith("SVar:")) {
|
||||||
|
actualsVar = actualsVar.split("SVar:")[1];
|
||||||
|
name = actualsVar.split(":")[0];
|
||||||
|
actualsVar = actualsVar.split(":")[1];
|
||||||
}
|
}
|
||||||
|
sVarsMap.put(name, actualsVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
List<Card> tgts = getCardsfromTargets(sa);
|
List<Card> tgts = getCardsfromTargets(sa);
|
||||||
|
|
||||||
@@ -151,7 +164,7 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
? TextUtil.fastReplace(sa.getParam("OptionQuestion"), "TARGETS", targets)
|
? TextUtil.fastReplace(sa.getParam("OptionQuestion"), "TARGETS", targets)
|
||||||
: getStackDescription(sa);
|
: getStackDescription(sa);
|
||||||
|
|
||||||
if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, message)) {
|
if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, message, null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,15 +179,8 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// give sVars
|
// give sVars
|
||||||
for (final String s : sVars) {
|
if (!sVarsMap.isEmpty()) {
|
||||||
String actualsVar = AbilityUtils.getSVar(sa, s);
|
c.addChangedSVars(sVarsMap, timestamp, 0);
|
||||||
String name = s;
|
|
||||||
if (actualsVar.startsWith("SVar:")) {
|
|
||||||
actualsVar = actualsVar.split("SVar:")[1];
|
|
||||||
name = actualsVar.split(":")[0];
|
|
||||||
actualsVar = actualsVar.split(":")[1];
|
|
||||||
}
|
|
||||||
c.setSVar(name, actualsVar);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// give Remembered
|
// give Remembered
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
public void run() {
|
public void run() {
|
||||||
doUnanimate(c, timestamp);
|
doUnanimate(c, timestamp);
|
||||||
|
|
||||||
|
c.removeChangedSVars(timestamp, 0);
|
||||||
c.removeChangedName(timestamp, 0);
|
c.removeChangedName(timestamp, 0);
|
||||||
c.updateStateForView();
|
c.updateStateForView();
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ public class AttachEffect extends SpellAbilityEffect {
|
|||||||
// If Cast Targets will be checked on the Stack
|
// If Cast Targets will be checked on the Stack
|
||||||
for (final Card attachment : attachments) {
|
for (final Card attachment : attachments) {
|
||||||
String message = Localizer.getInstance().getMessage("lblDoYouWantAttachSourceToTarget", CardTranslation.getTranslatedName(attachment.getName()), attachToName);
|
String message = Localizer.getInstance().getMessage("lblDoYouWantAttachSourceToTarget", CardTranslation.getTranslatedName(attachment.getName()), attachToName);
|
||||||
if (sa.hasParam("Optional") && !p.getController().confirmAction(sa, null, message))
|
if (sa.hasParam("Optional") && !p.getController().confirmAction(sa, null, message, null))
|
||||||
// TODO add params for message
|
// TODO add params for message
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ package forge.game.ability.effects;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.util.Lang;
|
||||||
|
|
||||||
public class BecomeMonarchEffect extends SpellAbilityEffect {
|
public class BecomeMonarchEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -16,8 +15,8 @@ public class BecomeMonarchEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||||
|
|
||||||
sb.append(StringUtils.join(tgtPlayers, ", "));
|
sb.append(Lang.joinHomogenous(tgtPlayers)).append(tgtPlayers.size() == 1 ? " becomes" : " become");
|
||||||
sb.append(" becomes the Monarch.");
|
sb.append(" the monarch.");
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import forge.game.spellability.SpellAbilityStackInstance;
|
|||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.util.CardTranslation;
|
import forge.util.CardTranslation;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
public class ChangeCombatantsEffect extends SpellAbilityEffect {
|
public class ChangeCombatantsEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -47,7 +47,8 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect {
|
|||||||
if ((tgt == null) || c.canBeTargetedBy(sa)) {
|
if ((tgt == null) || c.canBeTargetedBy(sa)) {
|
||||||
final Combat combat = game.getCombat();
|
final Combat combat = game.getCombat();
|
||||||
final GameEntity originalDefender = combat.getDefenderByAttacker(c);
|
final GameEntity originalDefender = combat.getDefenderByAttacker(c);
|
||||||
final FCollectionView<GameEntity> defs = combat.getDefenders();
|
final FCollection<GameEntity> defs = new FCollection<>();
|
||||||
|
defs.addAll(sa.hasParam("PlayerOnly") ? combat.getDefendingPlayers() : combat.getDefenders());
|
||||||
|
|
||||||
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName()));
|
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName()));
|
||||||
Map<String, Object> params = Maps.newHashMap();
|
Map<String, Object> params = Maps.newHashMap();
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
|||||||
// Redirect rules read 'you MAY choose new targets' ... okay!
|
// Redirect rules read 'you MAY choose new targets' ... okay!
|
||||||
// TODO: Don't even ask to change targets, if the SA and subs don't actually have targets
|
// TODO: Don't even ask to change targets, if the SA and subs don't actually have targets
|
||||||
boolean isOptional = sa.hasParam("Optional");
|
boolean isOptional = sa.hasParam("Optional");
|
||||||
if (isOptional && !chooser.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantChangeAbilityTargets", tgtSA.getHostCard().toString()))) {
|
if (isOptional && !chooser.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantChangeAbilityTargets", tgtSA.getHostCard().toString()), null)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (sa.hasParam("ChangeSingleTarget")) {
|
if (sa.hasParam("ChangeSingleTarget")) {
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
|||||||
message = Localizer.getInstance().getMessage("lblMoveTargetFromOriginToDestination", targets, Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME), destination.getTranslatedName());
|
message = Localizer.getInstance().getMessage("lblMoveTargetFromOriginToDestination", targets, Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME), destination.getTranslatedName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, message)) {
|
if (!sa.getActivatingPlayer().getController().confirmAction(sa, null, message, null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,8 +196,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
if (destination.equals("Battlefield")) {
|
if (destination.equals("Battlefield")) {
|
||||||
sb.append("onto the battlefield");
|
sb.append("onto the battlefield");
|
||||||
if (tapped) {
|
if (tapped) {
|
||||||
sb.append(" tapped");
|
sb.append(" tapped").append(attacking ? " and" : "");
|
||||||
}
|
}
|
||||||
|
sb.append(attacking ? " attacking" : "");
|
||||||
if (sa.hasParam("GainControl")) {
|
if (sa.hasParam("GainControl")) {
|
||||||
sb.append(" under ").append(chooserNames).append("'s control");
|
sb.append(" under ").append(chooserNames).append("'s control");
|
||||||
}
|
}
|
||||||
@@ -259,14 +260,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
if (destination.equals("Battlefield")) {
|
if (destination.equals("Battlefield")) {
|
||||||
sb.append(" onto the battlefield");
|
sb.append(" onto the battlefield");
|
||||||
if (tapped) {
|
if (tapped) {
|
||||||
sb.append(" tapped");
|
sb.append(" tapped").append(attacking ? " and" : "");
|
||||||
if (attacking) {
|
|
||||||
sb.append(" and");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (attacking) {
|
|
||||||
sb.append(" attacking");
|
|
||||||
}
|
}
|
||||||
|
sb.append(attacking ? " attacking" : "");
|
||||||
if (sa.hasParam("GainControl")) {
|
if (sa.hasParam("GainControl")) {
|
||||||
sb.append(" under ").append(chooserNames).append("'s control");
|
sb.append(" under ").append(chooserNames).append("'s control");
|
||||||
}
|
}
|
||||||
@@ -366,14 +362,16 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
final String fromGraveyard = " from the graveyard";
|
final String fromGraveyard = " from the graveyard";
|
||||||
|
|
||||||
if (destination.equals(ZoneType.Battlefield)) {
|
if (destination.equals(ZoneType.Battlefield)) {
|
||||||
|
final boolean attacking = (sa.hasParam("Attacking"));
|
||||||
if (ZoneType.Graveyard.equals(origin)) {
|
if (ZoneType.Graveyard.equals(origin)) {
|
||||||
sb.append("Return").append(targetname).append(fromGraveyard).append(" to the battlefield");
|
sb.append("Return").append(targetname).append(fromGraveyard).append(" to the battlefield");
|
||||||
} else {
|
} else {
|
||||||
sb.append("Put").append(targetname).append(" onto the battlefield");
|
sb.append("Put").append(targetname).append(" onto the battlefield");
|
||||||
}
|
}
|
||||||
if (sa.hasParam("Tapped")) {
|
if (sa.hasParam("Tapped")) {
|
||||||
sb.append(" tapped");
|
sb.append(" tapped").append(attacking ? " and" : "");
|
||||||
}
|
}
|
||||||
|
sb.append(attacking ? " attacking" : "");
|
||||||
if (sa.hasParam("GainControl")) {
|
if (sa.hasParam("GainControl")) {
|
||||||
sb.append(" under your control");
|
sb.append(" under your control");
|
||||||
}
|
}
|
||||||
@@ -494,7 +492,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
PlayerCollection deciders = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("AlternativeDecider"), sa);
|
PlayerCollection deciders = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("AlternativeDecider"), sa);
|
||||||
alterDecider = deciders.isEmpty() ? null : deciders.get(0);
|
alterDecider = deciders.isEmpty() ? null : deciders.get(0);
|
||||||
}
|
}
|
||||||
if (alterDecider != null && !alterDecider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneToAltDestination, sb.toString())) {
|
if (alterDecider != null && !alterDecider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneToAltDestination, sb.toString(), null)) {
|
||||||
destination = ZoneType.smartValueOf(sa.getParam("DestinationAlternative"));
|
destination = ZoneType.smartValueOf(sa.getParam("DestinationAlternative"));
|
||||||
altDest = true;
|
altDest = true;
|
||||||
}
|
}
|
||||||
@@ -513,7 +511,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFromStack(tgtSA, sa, si, game, triggerList);
|
removeFromStack(tgtSA, sa, si, game, triggerList, counterTable);
|
||||||
} // End of change from stack
|
} // End of change from stack
|
||||||
|
|
||||||
final String remember = sa.getParam("RememberChanged");
|
final String remember = sa.getParam("RememberChanged");
|
||||||
@@ -566,7 +564,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final String prompt = TextUtil.concatWithSpace(Localizer.getInstance().getMessage("lblDoYouWantMoveTargetFromOriToDest", CardTranslation.getTranslatedName(gameCard.getName()), Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME), destination.getTranslatedName()));
|
final String prompt = TextUtil.concatWithSpace(Localizer.getInstance().getMessage("lblDoYouWantMoveTargetFromOriToDest", CardTranslation.getTranslatedName(gameCard.getName()), Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME), destination.getTranslatedName()));
|
||||||
if (optional && !chooser.getController().confirmAction(sa, null, prompt) )
|
if (optional && !chooser.getController().confirmAction(sa, null, prompt, null) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
final Zone originZone = game.getZoneOf(gameCard);
|
final Zone originZone = game.getZoneOf(gameCard);
|
||||||
@@ -958,7 +956,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
sb.append(sa.getParam("AlternativeMessage")).append(" ");
|
sb.append(sa.getParam("AlternativeMessage")).append(" ");
|
||||||
sb.append(altFetchList.size()).append(" " + Localizer.getInstance().getMessage("lblCardMatchSearchingTypeInAlternateZones"));
|
sb.append(altFetchList.size()).append(" " + Localizer.getInstance().getMessage("lblCardMatchSearchingTypeInAlternateZones"));
|
||||||
|
|
||||||
if (!decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneFromAltSource, sb.toString())) {
|
if (!decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneFromAltSource, sb.toString(), null)) {
|
||||||
origin = alt;
|
origin = alt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -970,7 +968,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
sb.append(sa.getParam("AlternativeDestinationMessage"));
|
sb.append(sa.getParam("AlternativeDestinationMessage"));
|
||||||
|
|
||||||
if (!decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneToAltDestination, sb.toString())) {
|
if (!decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneToAltDestination, sb.toString(), null)) {
|
||||||
destination = ZoneType.smartValueOf(sa.getParam("DestinationAlternative"));
|
destination = ZoneType.smartValueOf(sa.getParam("DestinationAlternative"));
|
||||||
libraryPos = sa.hasParam("LibraryPositionAlternative") ? Integer.parseInt(sa.getParam("LibraryPositionAlternative")) : 0;
|
libraryPos = sa.hasParam("LibraryPositionAlternative") ? Integer.parseInt(sa.getParam("LibraryPositionAlternative")) : 0;
|
||||||
}
|
}
|
||||||
@@ -987,7 +985,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
prompt = Localizer.getInstance().getMessage("lblSearchPlayerZoneConfirm", "{player's}", Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME).toLowerCase());
|
prompt = Localizer.getInstance().getMessage("lblSearchPlayerZoneConfirm", "{player's}", Lang.joinHomogenous(origin, ZoneType.Accessors.GET_TRANSLATED_NAME).toLowerCase());
|
||||||
}
|
}
|
||||||
String message = MessageUtil.formatMessage(prompt , decider, player);
|
String message = MessageUtil.formatMessage(prompt , decider, player);
|
||||||
if (!decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneGeneral, message)) {
|
if (!decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneGeneral, message, null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1073,7 +1071,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SpellAbility tgtSA = decider.getController().getAbilityToPlay(tgtCard, sas);
|
SpellAbility tgtSA = decider.getController().getAbilityToPlay(tgtCard, sas);
|
||||||
if (!decider.getController().confirmAction(tgtSA, null, Localizer.getInstance().getMessage("lblDoYouWantPlayCard", CardTranslation.getTranslatedName(tgtCard.getName())))) {
|
if (!decider.getController().confirmAction(tgtSA, null, Localizer.getInstance().getMessage("lblDoYouWantPlayCard", CardTranslation.getTranslatedName(tgtCard.getName())), null)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
tgtSA.setSVar("IsCastFromPlayEffect", "True");
|
tgtSA.setSVar("IsCastFromPlayEffect", "True");
|
||||||
@@ -1189,7 +1187,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
String message = Localizer.getInstance().getMessage("lblCancelSearchUpToSelectNumCards", String.valueOf(num));
|
String message = Localizer.getInstance().getMessage("lblCancelSearchUpToSelectNumCards", String.valueOf(num));
|
||||||
|
|
||||||
if (fetchList.isEmpty() || sa.hasParam("SkipCancelPrompt") ||
|
if (fetchList.isEmpty() || sa.hasParam("SkipCancelPrompt") ||
|
||||||
decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneGeneral, message)) {
|
decider.getController().confirmAction(sa, PlayerActionConfirmMode.ChangeZoneGeneral, message, null)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i--;
|
i--;
|
||||||
@@ -1545,7 +1543,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
* object.
|
* object.
|
||||||
* @param game
|
* @param game
|
||||||
*/
|
*/
|
||||||
private static void removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si, final Game game, CardZoneTable triggerList) {
|
private static void removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si, final Game game, CardZoneTable triggerList, GameEntityCounterTable counterTable) {
|
||||||
final Card tgtHost = tgtSA.getHostCard();
|
final Card tgtHost = tgtSA.getHostCard();
|
||||||
final Zone originZone = tgtHost.getZone();
|
final Zone originZone = tgtHost.getZone();
|
||||||
game.getStack().remove(si);
|
game.getStack().remove(si);
|
||||||
@@ -1557,7 +1555,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
Card movedCard = null;
|
Card movedCard = null;
|
||||||
if (srcSA.hasParam("Destination")) {
|
if (srcSA.hasParam("Destination")) {
|
||||||
final boolean remember = srcSA.hasParam("RememberChanged");
|
final boolean remember = srcSA.hasParam("RememberChanged");
|
||||||
final boolean rememberSpell = srcSA.hasParam("RememberSpell");
|
|
||||||
final boolean imprint = srcSA.hasParam("Imprint");
|
final boolean imprint = srcSA.hasParam("Imprint");
|
||||||
if (tgtSA.isAbility()) {
|
if (tgtSA.isAbility()) {
|
||||||
// Shouldn't be able to target Abilities but leaving this in for now
|
// Shouldn't be able to target Abilities but leaving this in for now
|
||||||
@@ -1589,13 +1586,20 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
+ srcSA.getHostCard().getName());
|
+ srcSA.getHostCard().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (srcSA.hasParam("WithCountersType")) {
|
||||||
|
Player placer = srcSA.getActivatingPlayer();
|
||||||
|
if (srcSA.hasParam("WithCountersPlacer")) {
|
||||||
|
placer = AbilityUtils.getDefinedPlayers(srcSA.getHostCard(), srcSA.getParam("WithCountersPlacer"), srcSA).get(0);
|
||||||
|
}
|
||||||
|
CounterType cType = CounterType.getType(srcSA.getParam("WithCountersType"));
|
||||||
|
int cAmount = AbilityUtils.calculateAmount(srcSA.getHostCard(), srcSA.getParamOrDefault("WithCountersAmount", "1"), srcSA);
|
||||||
|
movedCard.addCounter(cType, cAmount, placer, counterTable);
|
||||||
|
}
|
||||||
|
|
||||||
if (remember) {
|
if (remember) {
|
||||||
srcSA.getHostCard().addRemembered(tgtHost);
|
srcSA.getHostCard().addRemembered(tgtHost);
|
||||||
// TODO or remember moved?
|
// TODO or remember moved?
|
||||||
}
|
}
|
||||||
if (rememberSpell) {
|
|
||||||
srcSA.getHostCard().addRemembered(tgtSA);
|
|
||||||
}
|
|
||||||
if (imprint) {
|
if (imprint) {
|
||||||
srcSA.getHostCard().addImprintedCard(tgtHost);
|
srcSA.getHostCard().addImprintedCard(tgtHost);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ public class CharmEffect extends SpellAbilityEffect {
|
|||||||
num = Math.min(num, choices.size());
|
num = Math.min(num, choices.size());
|
||||||
|
|
||||||
boolean isOptional = sa.hasParam("Optional");
|
boolean isOptional = sa.hasParam("Optional");
|
||||||
if (isOptional && !activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblWouldYouLikeCharm", CardTranslation.getTranslatedName(source.getName())))) {
|
if (isOptional && !activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblWouldYouLikeCharm", CardTranslation.getTranslatedName(source.getName())), null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
|
|||||||
Localizer.getInstance().getMessage("lblSelectCreatureWithTotalPowerLessOrEqualTo", (totP - chosenP - negativeNum))
|
Localizer.getInstance().getMessage("lblSelectCreatureWithTotalPowerLessOrEqualTo", (totP - chosenP - negativeNum))
|
||||||
+ "\r\n(" + Localizer.getInstance().getMessage("lblSelected") + ":" + chosenPool + ")\r\n(" + Localizer.getInstance().getMessage("lblTotalPowerNum", chosenP) + ")", chosenP <= totP, null);
|
+ "\r\n(" + Localizer.getInstance().getMessage("lblSelected") + ":" + chosenPool + ")\r\n(" + Localizer.getInstance().getMessage("lblTotalPowerNum", chosenP) + ")", chosenP <= totP, null);
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
if (p.getController().confirmAction(sa, PlayerActionConfirmMode.OptionalChoose, Localizer.getInstance().getMessage("lblCancelChooseConfirm"))) {
|
if (p.getController().confirmAction(sa, PlayerActionConfirmMode.OptionalChoose, Localizer.getInstance().getMessage("lblCancelChooseConfirm"), null)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean randomChoice = sa.hasParam("AtRandom");
|
boolean randomChoice = sa.hasParam("AtRandom");
|
||||||
boolean draft = sa.hasParam("Draft"); //for digital "draft from spellbook" mechanic
|
|
||||||
boolean chooseFromDefined = sa.hasParam("ChooseFromDefinedCards");
|
boolean chooseFromDefined = sa.hasParam("ChooseFromDefinedCards");
|
||||||
boolean chooseFromList = sa.hasParam("ChooseFromList");
|
boolean chooseFromList = sa.hasParam("ChooseFromList");
|
||||||
boolean chooseFromOneTimeList = sa.hasParam("ChooseFromOneTimeList");
|
boolean chooseFromOneTimeList = sa.hasParam("ChooseFromOneTimeList");
|
||||||
@@ -62,8 +61,6 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
|||||||
if (!randomChoice) {
|
if (!randomChoice) {
|
||||||
if (sa.hasParam("SelectPrompt")) {
|
if (sa.hasParam("SelectPrompt")) {
|
||||||
message = sa.getParam("SelectPrompt");
|
message = sa.getParam("SelectPrompt");
|
||||||
} else if (draft) {
|
|
||||||
message = Localizer.getInstance().getMessage("lblChooseCardDraft");
|
|
||||||
} else if (null == validDesc) {
|
} else if (null == validDesc) {
|
||||||
message = Localizer.getInstance().getMessage("lblChooseACardName");
|
message = Localizer.getInstance().getMessage("lblChooseACardName");
|
||||||
} else {
|
} else {
|
||||||
@@ -108,12 +105,6 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
|||||||
chosen = p.getController().chooseCardName(sa, faces, message);
|
chosen = p.getController().chooseCardName(sa, faces, message);
|
||||||
} else if (chooseFromList) {
|
} else if (chooseFromList) {
|
||||||
String [] names = sa.getParam("ChooseFromList").split(",");
|
String [] names = sa.getParam("ChooseFromList").split(",");
|
||||||
if (sa.hasParam("Draft")) {
|
|
||||||
List<String> options = Arrays.asList(names);
|
|
||||||
Collections.shuffle(options);
|
|
||||||
List<String> draftChoices = options.subList(0,3);
|
|
||||||
names = draftChoices.toArray(new String[0]);
|
|
||||||
}
|
|
||||||
List<ICardFace> faces = new ArrayList<>();
|
List<ICardFace> faces = new ArrayList<>();
|
||||||
for (String name : names) {
|
for (String name : names) {
|
||||||
// Cardnames that include "," must use ";" instead in ChooseFromList$ (i.e. Tovolar; Dire Overlord)
|
// Cardnames that include "," must use ";" instead in ChooseFromList$ (i.e. Tovolar; Dire Overlord)
|
||||||
@@ -169,9 +160,6 @@ public class ChooseCardNameEffect extends SpellAbilityEffect {
|
|||||||
host.setNamedCard(chosen);
|
host.setNamedCard(chosen);
|
||||||
if (!randomChoice) {
|
if (!randomChoice) {
|
||||||
p.setNamedCard(chosen);
|
p.setNamedCard(chosen);
|
||||||
if (!draft) { //drafting is secret
|
|
||||||
p.getGame().getAction().notifyOfValue(sa, host, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), chosen), p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (sa.hasParam("NoteFor")) {
|
if (sa.hasParam("NoteFor")) {
|
||||||
p.addNoteForName(sa.getParam("NoteFor"), "Name:" + chosen);
|
p.addNoteForName(sa.getParam("NoteFor"), "Name:" + chosen);
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public class CloneEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final boolean optional = sa.hasParam("Optional");
|
final boolean optional = sa.hasParam("Optional");
|
||||||
if (optional && !host.getController().getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantCopy", CardTranslation.getTranslatedName(cardToCopy.getName())))) {
|
if (optional && !host.getController().getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantCopy", CardTranslation.getTranslatedName(cardToCopy.getName())), null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ import forge.game.player.Player;
|
|||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Lang;
|
import forge.util.Lang;
|
||||||
|
import forge.util.Localizer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -24,14 +27,15 @@ public class ConniveEffect extends SpellAbilityEffect {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
List<Card> tgt = getTargetCards(sa);
|
List<Card> tgt = getTargetCards(sa);
|
||||||
|
if (tgt.size() <= 0) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
sb.append(Lang.joinHomogenous(tgt)).append(tgt.size() > 1 ? " connive." : " connives.");
|
sb.append(Lang.joinHomogenous(tgt)).append(tgt.size() > 1 ? " connive." : " connives.");
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.game.ability.SpellAbilityEffect#resolve(forge.game.spellability.SpellAbility)
|
* @see forge.game.ability.SpellAbilityEffect#resolve(forge.game.spellability.SpellAbility)
|
||||||
@@ -39,10 +43,30 @@ public class ConniveEffect 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 hostCon = host.getController();
|
|
||||||
final Game game = host.getGame();
|
final Game game = host.getGame();
|
||||||
final int num = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("ConniveNum", "1"), sa);
|
final int num = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("ConniveNum", "1"), sa);
|
||||||
|
|
||||||
|
CardCollection toConnive = getTargetCards(sa);
|
||||||
|
if (toConnive.isEmpty()) { // if nothing is conniving, we're done
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Player> controllers = new ArrayList<>();
|
||||||
|
for (Card c : toConnive) {
|
||||||
|
final Player controller = c.getController();
|
||||||
|
if (!controllers.contains(controller)) {
|
||||||
|
controllers.add(controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//order controllers by APNAP
|
||||||
|
int indexAP = controllers.indexOf(game.getPhaseHandler().getPlayerTurn());
|
||||||
|
if (indexAP != -1) {
|
||||||
|
Collections.rotate(controllers, - indexAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Player p : controllers) {
|
||||||
|
CardCollection connivers = CardLists.filterControlledBy(toConnive, p);
|
||||||
|
while (connivers.size() > 0) {
|
||||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
GameEntityCounterTable table = new GameEntityCounterTable();
|
||||||
final CardZoneTable triggerList = new CardZoneTable();
|
final CardZoneTable triggerList = new CardZoneTable();
|
||||||
Map<Player, CardCollectionView> discardedMap = Maps.newHashMap();
|
Map<Player, CardCollectionView> discardedMap = Maps.newHashMap();
|
||||||
@@ -50,44 +74,39 @@ public class ConniveEffect extends SpellAbilityEffect {
|
|||||||
moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield());
|
moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield());
|
||||||
moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard());
|
moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard());
|
||||||
|
|
||||||
for (final Card c : getTargetCards(sa)) {
|
Card conniver = connivers.size() > 1 ? p.getController().chooseSingleEntityForEffect(connivers, sa,
|
||||||
final Player p = c.getController();
|
Localizer.getInstance().getMessage("lblChooseConniver"), null) : connivers.get(0);
|
||||||
|
|
||||||
p.drawCards(num, sa, moveParams);
|
p.drawCards(num, sa, moveParams);
|
||||||
|
|
||||||
CardCollectionView dPHand = p.getCardsIn(ZoneType.Hand);
|
CardCollection validDisards =
|
||||||
dPHand = CardLists.filter(dPHand, CardPredicates.Presets.NON_TOKEN);
|
CardLists.filter(p.getCardsIn(ZoneType.Hand), CardPredicates.Presets.NON_TOKEN);
|
||||||
if (dPHand.isEmpty()) { // seems unlikely, but just to be safe
|
if (validDisards.isEmpty() || !p.canDiscardBy(sa, true)) { // hand being empty unlikely, just to be safe
|
||||||
continue; // for loop over players
|
|
||||||
}
|
|
||||||
|
|
||||||
CardCollection validCards = CardLists.getValidCards(dPHand, "Card", hostCon, host, sa);
|
|
||||||
|
|
||||||
if (!p.canDiscardBy(sa, true)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int amt = Math.min(validCards.size(), num);
|
int amt = Math.min(validDisards.size(), num);
|
||||||
CardCollectionView toBeDiscarded = amt == 0 ? CardCollection.EMPTY :
|
CardCollectionView toBeDiscarded = amt == 0 ? CardCollection.EMPTY :
|
||||||
p.getController().chooseCardsToDiscardFrom(p, sa, validCards, amt, amt);
|
p.getController().chooseCardsToDiscardFrom(p, sa, validDisards, amt, amt);
|
||||||
|
|
||||||
if (toBeDiscarded.size() > 1) {
|
if (toBeDiscarded.size() > 1) {
|
||||||
toBeDiscarded = GameActionUtil.orderCardsByTheirOwners(game, toBeDiscarded, ZoneType.Graveyard, sa);
|
toBeDiscarded = GameActionUtil.orderCardsByTheirOwners(game, toBeDiscarded, ZoneType.Graveyard, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
discardedMap.put(p, toBeDiscarded);
|
int numCntrs = CardLists.getValidCardCount(toBeDiscarded, "Card.nonLand", p, host, sa);
|
||||||
|
|
||||||
int numCntrs = CardLists.getValidCardCount(toBeDiscarded, "Card.nonLand", hostCon, host, sa);
|
|
||||||
|
|
||||||
// need to get newest game state to check if it is still on the battlefield and the timestamp didn't change
|
// need to get newest game state to check if it is still on the battlefield and the timestamp didn't change
|
||||||
Card gamec = game.getCardState(c);
|
Card gamec = game.getCardState(conniver);
|
||||||
// if the card is not in the game anymore, this might still return true, but it's no problem
|
// if the card is not in the game anymore, this might still return true, but it's no problem
|
||||||
if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.equalsWithTimestamp(c)) {
|
if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.equalsWithTimestamp(conniver)) {
|
||||||
c.addCounter(CounterEnumType.P1P1, numCntrs, p, table);
|
conniver.addCounter(CounterEnumType.P1P1, numCntrs, p, table);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
connivers.remove(conniver);
|
||||||
|
discardedMap.put(p, CardCollection.getView(toBeDiscarded));
|
||||||
discard(sa, triggerList, true, discardedMap, moveParams);
|
discard(sa, triggerList, true, discardedMap, moveParams);
|
||||||
table.replaceCounterEffect(game, sa, true);
|
table.replaceCounterEffect(game, sa, true);
|
||||||
triggerList.triggerChangesZoneAll(game, sa);
|
triggerList.triggerChangesZoneAll(game, sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class ControlExchangeEffect extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("Optional") && !sa.getActivatingPlayer().getController().confirmAction(sa, null,
|
if (sa.hasParam("Optional") && !sa.getActivatingPlayer().getController().confirmAction(sa, null,
|
||||||
Localizer.getInstance().getMessage("lblExchangeControl",
|
Localizer.getInstance().getMessage("lblExchangeControl",
|
||||||
CardTranslation.getTranslatedName(object1.getName()),
|
CardTranslation.getTranslatedName(object1.getName()),
|
||||||
CardTranslation.getTranslatedName(object2.getName())))) {
|
CardTranslation.getTranslatedName(object2.getName())), null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.GameCommand;
|
import forge.GameCommand;
|
||||||
@@ -16,9 +20,6 @@ import forge.game.spellability.SpellAbility;
|
|||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ControlGainEffect extends SpellAbilityEffect {
|
public class ControlGainEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -118,9 +119,7 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
|||||||
final Game game = newController.getGame();
|
final Game game = newController.getGame();
|
||||||
|
|
||||||
CardCollectionView tgtCards = null;
|
CardCollectionView tgtCards = null;
|
||||||
if (sa.hasParam("ControlledByTarget")) {
|
if (sa.hasParam("Choices")) {
|
||||||
tgtCards = CardLists.filterControlledBy(tgtCards, getTargetPlayers(sa));
|
|
||||||
} else if (sa.hasParam("Choices")) {
|
|
||||||
Player chooser = sa.hasParam("Chooser") ? AbilityUtils.getDefinedPlayers(source,
|
Player chooser = sa.hasParam("Chooser") ? AbilityUtils.getDefinedPlayers(source,
|
||||||
sa.getParam("Chooser"), sa).get(0) : activator;
|
sa.getParam("Chooser"), sa).get(0) : activator;
|
||||||
CardCollectionView choices = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield),
|
CardCollectionView choices = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield),
|
||||||
@@ -128,12 +127,16 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
|||||||
if (!choices.isEmpty()) {
|
if (!choices.isEmpty()) {
|
||||||
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") :
|
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") :
|
||||||
Localizer.getInstance().getMessage("lblChooseaCard") +" ";
|
Localizer.getInstance().getMessage("lblChooseaCard") +" ";
|
||||||
tgtCards = activator.getController().chooseCardsForEffect(choices, sa, title, 1, 1, false, null);
|
tgtCards = chooser.getController().chooseCardsForEffect(choices, sa, title, 1, 1, false, null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tgtCards = getDefinedCards(sa);
|
tgtCards = getDefinedCards(sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tgtCards != null & sa.hasParam("ControlledByTarget")) {
|
||||||
|
tgtCards = CardLists.filterControlledBy(tgtCards, getTargetPlayers(sa));
|
||||||
|
}
|
||||||
|
|
||||||
// in case source was LKI or still resolving
|
// in case source was LKI or still resolving
|
||||||
if (source.isLKI() || source.getZone().is(ZoneType.Stack)) {
|
if (source.isLKI() || source.getZone().is(ZoneType.Stack)) {
|
||||||
source = game.getCardState(source);
|
source = game.getCardState(source);
|
||||||
@@ -208,11 +211,11 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
if (lose.contains("EOT")) {
|
if (lose.contains("EOT")) {
|
||||||
game.getEndOfTurn().addUntil(loseControl);
|
game.getEndOfTurn().addUntil(loseControl);
|
||||||
tgtC.setSVar("SacMe", "6");
|
tgtC.addChangedSVars(Collections.singletonMap("SacMe", "6"), tStamp, 0);
|
||||||
}
|
}
|
||||||
if (lose.contains("EndOfCombat")) {
|
if (lose.contains("EndOfCombat")) {
|
||||||
game.getEndOfCombat().addUntil(loseControl);
|
game.getEndOfCombat().addUntil(loseControl);
|
||||||
tgtC.setSVar("SacMe", "6");
|
tgtC.addChangedSVars(Collections.singletonMap("SacMe", "6"), tStamp, 0);
|
||||||
}
|
}
|
||||||
if (lose.contains("StaticCommandCheck")) {
|
if (lose.contains("StaticCommandCheck")) {
|
||||||
String leftVar = sa.getSVar(sa.getParam("StaticCommandCheckSVar"));
|
String leftVar = sa.getSVar(sa.getParam("StaticCommandCheckSVar"));
|
||||||
@@ -274,7 +277,7 @@ public class ControlGainEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
doLoseControl(c, hostCard, bTapOnLose, tStamp);
|
doLoseControl(c, hostCard, bTapOnLose, tStamp);
|
||||||
c.removeSVar("SacMe");
|
c.removeChangedSVars(tStamp, 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user