mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
Merge branch 'master' into mutate
This commit is contained in:
@@ -23,7 +23,6 @@ import com.google.common.collect.Lists;
|
|||||||
import forge.ai.ability.AnimateAi;
|
import forge.ai.ability.AnimateAi;
|
||||||
import forge.card.CardTypeView;
|
import forge.card.CardTypeView;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.ability.effects.ProtectEffect;
|
import forge.game.ability.effects.ProtectEffect;
|
||||||
@@ -196,12 +195,12 @@ public class AiAttackController {
|
|||||||
for (Card c : ai.getOpponents().getCardsIn(ZoneType.Battlefield)) {
|
for (Card c : ai.getOpponents().getCardsIn(ZoneType.Battlefield)) {
|
||||||
for (Trigger t : c.getTriggers()) {
|
for (Trigger t : c.getTriggers()) {
|
||||||
if (t.getMode() == TriggerType.Attacks) {
|
if (t.getMode() == TriggerType.Attacks) {
|
||||||
SpellAbility sa = t.getOverridingAbility();
|
SpellAbility sa = t.ensureAbility();
|
||||||
if (sa == null && t.hasParam("Execute")) {
|
if (sa == null) {
|
||||||
sa = AbilityFactory.getAbility(c, t.getParam("Execute"));
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa != null && sa.getApi() == ApiType.EachDamage && "TriggeredAttacker".equals(sa.getParam("DefinedPlayers"))) {
|
if (sa.getApi() == ApiType.EachDamage && "TriggeredAttacker".equals(sa.getParam("DefinedPlayers"))) {
|
||||||
List<Card> valid = CardLists.getValidCards(c.getController().getCreaturesInPlay(), sa.getParam("ValidCards"), c.getController(), c, sa);
|
List<Card> valid = CardLists.getValidCards(c.getController().getCreaturesInPlay(), sa.getParam("ValidCards"), c.getController(), c, sa);
|
||||||
// TODO: this assumes that 1 damage is dealt per creature. Improve this to check the parameter/X to determine
|
// TODO: this assumes that 1 damage is dealt per creature. Improve this to check the parameter/X to determine
|
||||||
// how much damage is dealt by each of the creatures in the valid list.
|
// how much damage is dealt by each of the creatures in the valid list.
|
||||||
@@ -1349,9 +1348,9 @@ public class AiAttackController {
|
|||||||
if (!TriggerType.Exerted.equals(t.getMode())) {
|
if (!TriggerType.Exerted.equals(t.getMode())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SpellAbility sa = t.getOverridingAbility();
|
SpellAbility sa = t.ensureAbility();
|
||||||
if (sa == null) {
|
if (sa == null) {
|
||||||
sa = AbilityFactory.getAbility(c, t.getParam("Execute"));
|
continue;
|
||||||
}
|
}
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
sa.setActivatingPlayer(c.getController());
|
sa.setActivatingPlayer(c.getController());
|
||||||
|
|||||||
@@ -429,8 +429,7 @@ public class AiController {
|
|||||||
byte color = MagicColor.fromName(c);
|
byte color = MagicColor.fromName(c);
|
||||||
for (Card land : landList) {
|
for (Card land : landList) {
|
||||||
for (final SpellAbility m : ComputerUtilMana.getAIPlayableMana(land)) {
|
for (final SpellAbility m : ComputerUtilMana.getAIPlayableMana(land)) {
|
||||||
AbilityManaPart mp = m.getManaPart();
|
if (m.canProduce(MagicColor.toShortString(color))) {
|
||||||
if (mp.canProduce(MagicColor.toShortString(color), m)) {
|
|
||||||
return land;
|
return land;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -483,8 +482,7 @@ public class AiController {
|
|||||||
return land;
|
return land;
|
||||||
}
|
}
|
||||||
for (final SpellAbility m : ComputerUtilMana.getAIPlayableMana(land)) {
|
for (final SpellAbility m : ComputerUtilMana.getAIPlayableMana(land)) {
|
||||||
AbilityManaPart mp = m.getManaPart();
|
if (m.canProduce(MagicColor.toShortString(color))) {
|
||||||
if (mp.canProduce(MagicColor.toShortString(color), m)) {
|
|
||||||
return land;
|
return land;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import forge.card.ColorSet;
|
|||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.card.mana.ManaCostShard;
|
import forge.card.mana.ManaCostShard;
|
||||||
import forge.game.*;
|
import forge.game.*;
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
@@ -1468,15 +1467,13 @@ public class ComputerUtil {
|
|||||||
// Triggered abilities
|
// Triggered abilities
|
||||||
if (c.isCreature() && c.isInZone(ZoneType.Battlefield) && CombatUtil.canAttack(c)) {
|
if (c.isCreature() && c.isInZone(ZoneType.Battlefield) && CombatUtil.canAttack(c)) {
|
||||||
for (final Trigger t : c.getTriggers()) {
|
for (final Trigger t : c.getTriggers()) {
|
||||||
if ("Attacks".equals(t.getParam("Mode")) && t.hasParam("Execute")) {
|
if (TriggerType.Attacks.equals(t.getMode())) {
|
||||||
String exec = c.getSVar(t.getParam("Execute"));
|
SpellAbility sa = t.ensureAbility();
|
||||||
if (!exec.isEmpty()) {
|
if (sa == null) {
|
||||||
SpellAbility trigSa = AbilityFactory.getAbility(exec, c);
|
continue;
|
||||||
if (trigSa != null && trigSa.getApi() == ApiType.LoseLife
|
|
||||||
&& trigSa.getParamOrDefault("Defined", "").contains("Opponent")) {
|
|
||||||
trigSa.setHostCard(c);
|
|
||||||
damage += AbilityUtils.calculateAmount(trigSa.getHostCard(), trigSa.getParam("LifeAmount"), trigSa);
|
|
||||||
}
|
}
|
||||||
|
if (sa.getApi() == ApiType.LoseLife && sa.getParamOrDefault("Defined", "").contains("Opponent")) {
|
||||||
|
damage += AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("LifeAmount"), sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2075,9 +2072,8 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
for (Card c : lands) {
|
for (Card c : lands) {
|
||||||
for (SpellAbility sa : c.getManaAbilities()) {
|
for (SpellAbility sa : c.getManaAbilities()) {
|
||||||
AbilityManaPart abmana = sa.getManaPart();
|
|
||||||
for (byte col : MagicColor.WUBRG) {
|
for (byte col : MagicColor.WUBRG) {
|
||||||
if (abmana.canProduce(MagicColor.toLongString(col))) {
|
if (sa.canProduce(MagicColor.toLongString(col))) {
|
||||||
numProducers.get(col).add(c);
|
numProducers.get(col).add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2613,7 +2609,6 @@ public class ComputerUtil {
|
|||||||
theTriggers.addAll(c.getTriggers());
|
theTriggers.addAll(c.getTriggers());
|
||||||
}
|
}
|
||||||
for (Trigger trigger : theTriggers) {
|
for (Trigger trigger : theTriggers) {
|
||||||
Map<String, String> trigParams = trigger.getMapParams();
|
|
||||||
final Card source = trigger.getHostCard();
|
final Card source = trigger.getHostCard();
|
||||||
|
|
||||||
|
|
||||||
@@ -2623,25 +2618,23 @@ public class ComputerUtil {
|
|||||||
if (!trigger.requirementsCheck(game)) {
|
if (!trigger.requirementsCheck(game)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TriggerType mode = trigger.getMode();
|
if (trigger.getMode() != TriggerType.SpellCast) {
|
||||||
if (mode != TriggerType.SpellCast) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trigParams.containsKey("ValidCard")) {
|
if (trigger.hasParam("ValidCard")) {
|
||||||
if (!card.isValid(trigParams.get("ValidCard"), source.getController(), source, sa)) {
|
if (!card.isValid(trigger.getParam("ValidCard"), source.getController(), source, sa)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trigParams.containsKey("ValidActivatingPlayer")) {
|
if (trigger.hasParam("ValidActivatingPlayer")) {
|
||||||
if (!player.isValid(trigParams.get("ValidActivatingPlayer"), source.getController(), source, sa)) {
|
if (!player.isValid(trigger.getParam("ValidActivatingPlayer"), source.getController(), source, sa)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!trigParams.containsKey("Execute")) {
|
|
||||||
// fall back for OverridingAbility
|
// fall back for OverridingAbility
|
||||||
SpellAbility trigSa = trigger.getOverridingAbility();
|
SpellAbility trigSa = trigger.ensureAbility();
|
||||||
if (trigSa == null) {
|
if (trigSa == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -2663,34 +2656,6 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
damage += AbilityUtils.calculateAmount(source, trigSa.getParam("LifeAmount"), trigSa);
|
damage += AbilityUtils.calculateAmount(source, trigSa.getParam("LifeAmount"), trigSa);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
String ability = source.getSVar(trigParams.get("Execute"));
|
|
||||||
if (ability.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, String> abilityParams = AbilityFactory.getMapParams(ability);
|
|
||||||
if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("DealDamage"))
|
|
||||||
|| (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("DealDamage"))) {
|
|
||||||
if (!"TriggeredActivator".equals(abilityParams.get("Defined"))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!abilityParams.containsKey("NumDmg")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
damage += ComputerUtilCombat.predictDamageTo(player,
|
|
||||||
AbilityUtils.calculateAmount(source, abilityParams.get("NumDmg"), null), source, false);
|
|
||||||
} else if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("LoseLife"))
|
|
||||||
|| (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("LoseLife"))) {
|
|
||||||
if (!"TriggeredActivator".equals(abilityParams.get("Defined"))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!abilityParams.containsKey("LifeAmount")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
damage += AbilityUtils.calculateAmount(source, abilityParams.get("LifeAmount"), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return damage;
|
return damage;
|
||||||
@@ -2705,7 +2670,6 @@ public class ComputerUtil {
|
|||||||
theTriggers.addAll(card.getTriggers());
|
theTriggers.addAll(card.getTriggers());
|
||||||
}
|
}
|
||||||
for (Trigger trigger : theTriggers) {
|
for (Trigger trigger : theTriggers) {
|
||||||
Map<String, String> trigParams = trigger.getMapParams();
|
|
||||||
final Card source = trigger.getHostCard();
|
final Card source = trigger.getHostCard();
|
||||||
|
|
||||||
|
|
||||||
@@ -2715,25 +2679,23 @@ public class ComputerUtil {
|
|||||||
if (!trigger.requirementsCheck(game)) {
|
if (!trigger.requirementsCheck(game)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trigParams.containsKey("CheckOnTriggeredCard")
|
if (trigger.hasParam("CheckOnTriggeredCard")
|
||||||
&& AbilityUtils.getDefinedCards(permanent, source.getSVar(trigParams.get("CheckOnTriggeredCard").split(" ")[0]), null).isEmpty()) {
|
&& AbilityUtils.getDefinedCards(permanent, source.getSVar(trigger.getParam("CheckOnTriggeredCard").split(" ")[0]), null).isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TriggerType mode = trigger.getMode();
|
if (trigger.getMode() != TriggerType.ChangesZone) {
|
||||||
if (mode != TriggerType.ChangesZone) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!"Battlefield".equals(trigParams.get("Destination"))) {
|
if (!"Battlefield".equals(trigger.getParam("Destination"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trigParams.containsKey("ValidCard")) {
|
if (trigger.hasParam("ValidCard")) {
|
||||||
if (!permanent.isValid(trigParams.get("ValidCard"), source.getController(), source, null)) {
|
if (!permanent.isValid(trigger.getParam("ValidCard"), source.getController(), source, null)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!trigParams.containsKey("Execute")) {
|
|
||||||
// fall back for OverridingAbility
|
// fall back for OverridingAbility
|
||||||
SpellAbility trigSa = trigger.getOverridingAbility();
|
SpellAbility trigSa = trigger.ensureAbility();
|
||||||
if (trigSa == null) {
|
if (trigSa == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -2755,35 +2717,6 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
damage += AbilityUtils.calculateAmount(source, trigSa.getParam("LifeAmount"), trigSa);
|
damage += AbilityUtils.calculateAmount(source, trigSa.getParam("LifeAmount"), trigSa);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
String ability = source.getSVar(trigParams.get("Execute"));
|
|
||||||
if (ability.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, String> abilityParams = AbilityFactory.getMapParams(ability);
|
|
||||||
// Destroy triggers
|
|
||||||
if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("DealDamage"))
|
|
||||||
|| (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("DealDamage"))) {
|
|
||||||
if (!"TriggeredCardController".equals(abilityParams.get("Defined"))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!abilityParams.containsKey("NumDmg")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
damage += ComputerUtilCombat.predictDamageTo(player,
|
|
||||||
AbilityUtils.calculateAmount(source, abilityParams.get("NumDmg"), null), source, false);
|
|
||||||
} else if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("LoseLife"))
|
|
||||||
|| (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("LoseLife"))) {
|
|
||||||
if (!"TriggeredCardController".equals(abilityParams.get("Defined"))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!abilityParams.containsKey("LifeAmount")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
damage += AbilityUtils.calculateAmount(source, abilityParams.get("LifeAmount"), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import forge.deck.Deck;
|
|||||||
import forge.deck.DeckSection;
|
import forge.deck.DeckSection;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
@@ -697,31 +696,12 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
// same for Trigger that does make Tokens
|
// same for Trigger that does make Tokens
|
||||||
for(Trigger t:c.getTriggers()){
|
for(Trigger t:c.getTriggers()){
|
||||||
SpellAbility sa = t.getOverridingAbility();
|
SpellAbility sa = t.ensureAbility();
|
||||||
String sTokenTypes = null;
|
|
||||||
if (sa != null) {
|
if (sa != null) {
|
||||||
if (sa.getApi() != ApiType.Token || !sa.hasParam("TokenTypes")) {
|
if (sa.getApi() != ApiType.Token || !sa.hasParam("TokenTypes")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sTokenTypes = sa.getParam("TokenTypes");
|
for (String var : sa.getParam("TokenTypes").split(",")) {
|
||||||
} else if (t.hasParam("Execute")) {
|
|
||||||
String name = t.getParam("Execute");
|
|
||||||
if (!c.hasSVar(name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, String> params = AbilityFactory.getMapParams(c.getSVar(name));
|
|
||||||
if (!params.containsKey("TokenTypes")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
sTokenTypes = params.get("TokenTypes");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sTokenTypes == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String var : sTokenTypes.split(",")) {
|
|
||||||
if (!CardType.isACreatureType(var)) {
|
if (!CardType.isACreatureType(var)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -732,6 +712,7 @@ public class ComputerUtilCard {
|
|||||||
typesInDeck.put(var, count + 1);
|
typesInDeck.put(var, count + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// special rule for Fabricate and Servo
|
// special rule for Fabricate and Servo
|
||||||
if(c.hasStartOfKeyword(Keyword.FABRICATE.toString())){
|
if(c.hasStartOfKeyword(Keyword.FABRICATE.toString())){
|
||||||
Integer count = typesInDeck.get("Servo");
|
Integer count = typesInDeck.get("Servo");
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import com.google.common.collect.Maps;
|
|||||||
import forge.game.CardTraitBase;
|
import forge.game.CardTraitBase;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
@@ -46,7 +45,6 @@ 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.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerHandler;
|
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
@@ -1274,44 +1272,26 @@ public class ComputerUtilCombat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (final Trigger trigger : theTriggers) {
|
for (final Trigger trigger : theTriggers) {
|
||||||
final Map<String, String> trigParams = trigger.getMapParams();
|
|
||||||
final Card source = trigger.getHostCard();
|
final Card source = trigger.getHostCard();
|
||||||
|
|
||||||
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)) {
|
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> abilityParams = null;
|
SpellAbility sa = trigger.ensureAbility();
|
||||||
if (trigger.getOverridingAbility() != null) {
|
if (sa == null) {
|
||||||
abilityParams = trigger.getOverridingAbility().getMapParams();
|
|
||||||
} else if (trigParams.containsKey("Execute")) {
|
|
||||||
final String ability = source.getSVar(trigParams.get("Execute"));
|
|
||||||
abilityParams = AbilityFactory.getMapParams(ability);
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abilityParams.containsKey("ValidTgts") || abilityParams.containsKey("Tgt")) {
|
if (sa.usesTargeting()) {
|
||||||
continue; // targeted pumping not supported
|
continue; // targeted pumping not supported
|
||||||
}
|
}
|
||||||
if (abilityParams.containsKey("AB") && !abilityParams.get("AB").equals("Pump")
|
|
||||||
&& !abilityParams.get("AB").equals("PumpAll")) {
|
if (!ApiType.Pump.equals(sa.getApi()) && !ApiType.PumpAll.equals(sa.getApi())) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (abilityParams.containsKey("DB") && !abilityParams.get("DB").equals("Pump")
|
|
||||||
&& !abilityParams.get("DB").equals("PumpAll")) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abilityParams.containsKey("Cost")) {
|
if (sa.hasParam("Cost")) {
|
||||||
SpellAbility sa = null;
|
|
||||||
if (trigger.getOverridingAbility() != null) {
|
|
||||||
sa = trigger.getOverridingAbility();
|
|
||||||
} else {
|
|
||||||
final String ability = source.getSVar(trigParams.get("Execute"));
|
|
||||||
sa = AbilityFactory.getAbility(ability, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
sa.setActivatingPlayer(source.getController());
|
sa.setActivatingPlayer(source.getController());
|
||||||
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
|
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1319,15 +1299,15 @@ public class ComputerUtilCombat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Card> list = Lists.newArrayList();
|
List<Card> list = Lists.newArrayList();
|
||||||
if (!abilityParams.containsKey("ValidCards")) {
|
if (!sa.hasParam("ValidCards")) {
|
||||||
list = AbilityUtils.getDefinedCards(source, abilityParams.get("Defined"), null);
|
list = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), null);
|
||||||
}
|
}
|
||||||
if (abilityParams.containsKey("Defined") && abilityParams.get("Defined").equals("TriggeredAttacker")) {
|
if (sa.hasParam("Defined") && sa.getParam("Defined").equals("TriggeredAttacker")) {
|
||||||
list.add(attacker);
|
list.add(attacker);
|
||||||
}
|
}
|
||||||
if (abilityParams.containsKey("ValidCards")) {
|
if (sa.hasParam("ValidCards")) {
|
||||||
if (attacker.isValid(abilityParams.get("ValidCards").split(","), source.getController(), source, null)
|
if (attacker.isValid(sa.getParam("ValidCards").split(","), source.getController(), source, null)
|
||||||
|| attacker.isValid(abilityParams.get("ValidCards").replace("attacking+", "").split(","),
|
|| attacker.isValid(sa.getParam("ValidCards").replace("attacking+", "").split(","),
|
||||||
source.getController(), source, null)) {
|
source.getController(), source, null)) {
|
||||||
list.add(attacker);
|
list.add(attacker);
|
||||||
}
|
}
|
||||||
@@ -1338,11 +1318,11 @@ public class ComputerUtilCombat {
|
|||||||
if (!list.contains(attacker)) {
|
if (!list.contains(attacker)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!abilityParams.containsKey("NumAtt")) {
|
if (!sa.hasParam("NumAtt")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String att = abilityParams.get("NumAtt");
|
String att = sa.getParam("NumAtt");
|
||||||
if (att.startsWith("+")) {
|
if (att.startsWith("+")) {
|
||||||
att = att.substring(1);
|
att = att.substring(1);
|
||||||
}
|
}
|
||||||
@@ -1657,35 +1637,26 @@ public class ComputerUtilCombat {
|
|||||||
theTriggers.addAll(card.getTriggers());
|
theTriggers.addAll(card.getTriggers());
|
||||||
}
|
}
|
||||||
for (Trigger trigger : theTriggers) {
|
for (Trigger trigger : theTriggers) {
|
||||||
Map<String, String> trigParams = trigger.getMapParams();
|
|
||||||
final Card source = trigger.getHostCard();
|
final Card source = trigger.getHostCard();
|
||||||
|
|
||||||
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
|
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//consider delayed triggers
|
SpellAbility sa = trigger.ensureAbility();
|
||||||
if (trigParams.containsKey("DelayedTrigger")) {
|
if (sa == null) {
|
||||||
String sVarName = trigParams.get("DelayedTrigger");
|
|
||||||
trigger = TriggerHandler.parseTrigger(source.getSVar(sVarName), trigger.getHostCard(), true);
|
|
||||||
trigParams = trigger.getMapParams();
|
|
||||||
}
|
|
||||||
if (!trigParams.containsKey("Execute")) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String ability = source.getSVar(trigParams.get("Execute"));
|
if (ApiType.Destroy.equals(sa.getApi())) {
|
||||||
final Map<String, String> abilityParams = AbilityFactory.getMapParams(ability);
|
if (!sa.hasParam("Defined")) {
|
||||||
if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("Destroy"))
|
|
||||||
|| (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("Destroy"))) {
|
|
||||||
if (!abilityParams.containsKey("Defined")) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (abilityParams.get("Defined").equals("TriggeredAttacker")) {
|
if (sa.getParam("Defined").equals("TriggeredAttacker")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (abilityParams.get("Defined").equals("Self") && source.equals(attacker)) {
|
if (sa.getParam("Defined").equals("Self") && source.equals(attacker)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (abilityParams.get("Defined").equals("TriggeredTarget") && source.equals(blocker)) {
|
if (sa.getParam("Defined").equals("TriggeredTarget") && source.equals(blocker)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1935,36 +1906,27 @@ public class ComputerUtilCombat {
|
|||||||
theTriggers.addAll(card.getTriggers());
|
theTriggers.addAll(card.getTriggers());
|
||||||
}
|
}
|
||||||
for (Trigger trigger : theTriggers) {
|
for (Trigger trigger : theTriggers) {
|
||||||
Map<String, String> trigParams = trigger.getMapParams();
|
|
||||||
final Card source = trigger.getHostCard();
|
final Card source = trigger.getHostCard();
|
||||||
|
|
||||||
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
|
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//consider delayed triggers
|
SpellAbility sa = trigger.ensureAbility();
|
||||||
if (trigParams.containsKey("DelayedTrigger")) {
|
if (sa == null) {
|
||||||
String sVarName = trigParams.get("DelayedTrigger");
|
|
||||||
trigger = TriggerHandler.parseTrigger(source.getSVar(sVarName), trigger.getHostCard(), true);
|
|
||||||
trigParams = trigger.getMapParams();
|
|
||||||
}
|
|
||||||
if (!trigParams.containsKey("Execute")) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String ability = source.getSVar(trigParams.get("Execute"));
|
|
||||||
final Map<String, String> abilityParams = AbilityFactory.getMapParams(ability);
|
|
||||||
// Destroy triggers
|
// Destroy triggers
|
||||||
if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("Destroy"))
|
if (ApiType.Destroy.equals(sa.getApi())) {
|
||||||
|| (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("Destroy"))) {
|
if (!sa.hasParam("Defined")) {
|
||||||
if (!abilityParams.containsKey("Defined")) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (abilityParams.get("Defined").equals("TriggeredBlocker")) {
|
if (sa.getParam("Defined").equals("TriggeredBlocker")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (abilityParams.get("Defined").equals("Self") && source.equals(blocker)) {
|
if (sa.getParam("Defined").equals("Self") && source.equals(blocker)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (abilityParams.get("Defined").equals("TriggeredTarget") && source.equals(attacker)) {
|
if (sa.getParam("Defined").equals("TriggeredTarget") && source.equals(attacker)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2578,12 +2540,11 @@ public class ComputerUtilCombat {
|
|||||||
|
|
||||||
// Test for some special triggers that can change the creature in combat
|
// Test for some special triggers that can change the creature in combat
|
||||||
for (Trigger t : attacker.getTriggers()) {
|
for (Trigger t : attacker.getTriggers()) {
|
||||||
if (t.getMode() == TriggerType.Attacks && t.hasParam("Execute")) {
|
if (t.getMode() == TriggerType.Attacks) {
|
||||||
if (!attacker.hasSVar(t.getParam("Execute"))) {
|
SpellAbility exec = t.ensureAbility();
|
||||||
|
if (exec == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SpellAbility exec = AbilityFactory.getAbility(attacker, t.getParam("Execute"));
|
|
||||||
if (exec != null) {
|
|
||||||
if (exec.getApi() == ApiType.Clone && "Self".equals(exec.getParam("CloneTarget"))
|
if (exec.getApi() == ApiType.Clone && "Self".equals(exec.getParam("CloneTarget"))
|
||||||
&& exec.hasParam("ValidTgts") && exec.getParam("ValidTgts").contains("Creature")
|
&& exec.hasParam("ValidTgts") && exec.getParam("ValidTgts").contains("Creature")
|
||||||
&& exec.getParam("ValidTgts").contains("attacking")) {
|
&& exec.getParam("ValidTgts").contains("attacking")) {
|
||||||
@@ -2602,7 +2563,6 @@ public class ComputerUtilCombat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return attackerAfterTrigs;
|
return attackerAfterTrigs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,10 @@ import forge.card.mana.ManaAtom;
|
|||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.card.mana.ManaCostParser;
|
import forge.card.mana.ManaCostParser;
|
||||||
import forge.card.mana.ManaCostShard;
|
import forge.card.mana.ManaCostShard;
|
||||||
|
import forge.game.CardTraitPredicates;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameActionUtil;
|
import forge.game.GameActionUtil;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.*;
|
||||||
import forge.game.ability.AbilityUtils;
|
|
||||||
import forge.game.ability.ApiType;
|
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
import forge.game.cost.*;
|
import forge.game.cost.*;
|
||||||
@@ -25,10 +24,13 @@ import forge.game.phase.PhaseType;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
|
import forge.game.replacement.ReplacementLayer;
|
||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.AbilityManaPart;
|
import forge.game.spellability.AbilityManaPart;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.trigger.Trigger;
|
||||||
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
@@ -317,6 +319,190 @@ public class ComputerUtilMana {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String predictManaReplacement(SpellAbility saPayment, Player ai, ManaCostShard toPay) {
|
||||||
|
Card hostCard = saPayment.getHostCard();
|
||||||
|
Game game = hostCard.getGame();
|
||||||
|
String manaProduced = toPay.isSnow() && hostCard.isSnow() ? "S" : GameActionUtil.generatedTotalMana(saPayment);
|
||||||
|
//String originalProduced = manaProduced;
|
||||||
|
|
||||||
|
final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
|
||||||
|
repParams.put(AbilityKey.Mana, manaProduced);
|
||||||
|
repParams.put(AbilityKey.Affected, hostCard);
|
||||||
|
repParams.put(AbilityKey.Player, ai);
|
||||||
|
repParams.put(AbilityKey.AbilityMana, saPayment); // RootAbility
|
||||||
|
|
||||||
|
// TODO Damping Sphere might replace later?
|
||||||
|
|
||||||
|
// add flags to replacementEffects to filter better?
|
||||||
|
List<ReplacementEffect> reList = game.getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other);
|
||||||
|
|
||||||
|
List<SpellAbility> replaceMana = Lists.newArrayList();
|
||||||
|
List<SpellAbility> replaceType = Lists.newArrayList();
|
||||||
|
List<SpellAbility> replaceAmount = Lists.newArrayList(); // currently only multi
|
||||||
|
|
||||||
|
// try to guess the color the mana gets replaced to
|
||||||
|
for (ReplacementEffect re : reList) {
|
||||||
|
SpellAbility o = re.getOverridingAbility();
|
||||||
|
|
||||||
|
if (o == null || o.getApi() != ApiType.ReplaceMana) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this one does replace the amount too
|
||||||
|
if (o.hasParam("ReplaceMana")) {
|
||||||
|
replaceMana.add(o);
|
||||||
|
} else if (o.hasParam("ReplaceType") || o.hasParam("ReplaceColor")) {
|
||||||
|
// this one replaces the color/type
|
||||||
|
// check if this one can be replaced into wanted mana shard
|
||||||
|
replaceType.add(o);
|
||||||
|
} else if (o.hasParam("ReplaceAmount")) {
|
||||||
|
replaceAmount.add(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// it is better to apply these ones first
|
||||||
|
if (!replaceMana.isEmpty()) {
|
||||||
|
for (SpellAbility saMana : replaceMana) {
|
||||||
|
// one of then has to Any
|
||||||
|
// one of then has to C
|
||||||
|
// one of then has to B
|
||||||
|
String m = saMana.getParam("ReplaceMana");
|
||||||
|
if ("Any".equals(m)) {
|
||||||
|
byte rs = MagicColor.GREEN;
|
||||||
|
for (byte c : MagicColor.WUBRGC) {
|
||||||
|
if (toPay.canBePaidWithManaOfColor(c)) {
|
||||||
|
rs = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
manaProduced = MagicColor.toShortString(rs);
|
||||||
|
} else {
|
||||||
|
manaProduced = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// then apply this one
|
||||||
|
if (!replaceType.isEmpty()) {
|
||||||
|
for (SpellAbility saMana : replaceAmount) {
|
||||||
|
Card card = saMana.getHostCard();
|
||||||
|
if (saMana.hasParam("ReplaceType")) {
|
||||||
|
// replace color and colorless
|
||||||
|
String color = saMana.getParam("ReplaceType");
|
||||||
|
if ("Any".equals(color)) {
|
||||||
|
byte rs = MagicColor.GREEN;
|
||||||
|
for (byte c : MagicColor.WUBRGC) {
|
||||||
|
if (toPay.canBePaidWithManaOfColor(c)) {
|
||||||
|
rs = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color = MagicColor.toShortString(rs);
|
||||||
|
}
|
||||||
|
for (byte c : MagicColor.WUBRGC) {
|
||||||
|
String s = MagicColor.toShortString(c);
|
||||||
|
manaProduced = manaProduced.replace(s, color);
|
||||||
|
}
|
||||||
|
} else if (saMana.hasParam("ReplaceColor")) {
|
||||||
|
// replace color
|
||||||
|
String color = saMana.getParam("ReplaceColor");
|
||||||
|
if ("Chosen".equals(color)) {
|
||||||
|
if (card.hasChosenColor()) {
|
||||||
|
color = MagicColor.toShortString(card.getChosenColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (saMana.hasParam("ReplaceOnly")) {
|
||||||
|
manaProduced = manaProduced.replace(saMana.getParam("ReplaceOnly"), color);
|
||||||
|
} else {
|
||||||
|
for (byte c : MagicColor.WUBRG) {
|
||||||
|
String s = MagicColor.toShortString(c);
|
||||||
|
manaProduced = manaProduced.replace(s, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// then multiply if able
|
||||||
|
if (!replaceAmount.isEmpty()) {
|
||||||
|
int totalAmount = 1;
|
||||||
|
for (SpellAbility saMana : replaceAmount) {
|
||||||
|
totalAmount *= Integer.valueOf(saMana.getParam("ReplaceAmount"));
|
||||||
|
}
|
||||||
|
manaProduced = StringUtils.repeat(manaProduced, " ", totalAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return manaProduced;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String predictManafromSpellAbility(SpellAbility saPayment, Player ai, ManaCostShard toPay) {
|
||||||
|
Card hostCard = saPayment.getHostCard();
|
||||||
|
|
||||||
|
String manaProduced = predictManaReplacement(saPayment, ai, toPay);
|
||||||
|
String originalProduced = manaProduced;
|
||||||
|
|
||||||
|
if (originalProduced.isEmpty()) {
|
||||||
|
return manaProduced;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run triggers like Nissa
|
||||||
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(hostCard);
|
||||||
|
runParams.put(AbilityKey.Player, ai); // assuming AI would only ever gives itself mana
|
||||||
|
runParams.put(AbilityKey.AbilityMana, saPayment);
|
||||||
|
runParams.put(AbilityKey.Produced, manaProduced);
|
||||||
|
runParams.put(AbilityKey.Activator, ai);
|
||||||
|
for (Trigger tr : ai.getGame().getTriggerHandler().getActiveTrigger(TriggerType.TapsForMana, runParams)) {
|
||||||
|
SpellAbility trSA = tr.ensureAbility();
|
||||||
|
if (trSA == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ApiType.Mana.equals(trSA.getApi())) {
|
||||||
|
int pAmount = trSA.hasParam("Amount") ? Integer.valueOf(trSA.getParam("Amount")) : 1;
|
||||||
|
String produced = trSA.getParam("Produced");
|
||||||
|
if (produced.equals("Chosen")) {
|
||||||
|
produced = MagicColor.toShortString(trSA.getHostCard().getChosenColor());
|
||||||
|
}
|
||||||
|
manaProduced += " " + StringUtils.repeat(produced, pAmount);
|
||||||
|
} else if (ApiType.ManaReflected.equals(trSA.getApi())) {
|
||||||
|
final String colorOrType = trSA.getParamOrDefault("ColorOrType", "Color");
|
||||||
|
// currently Color or Type, Type is colors + colorless
|
||||||
|
final String reflectProperty = trSA.getParam("ReflectProperty");
|
||||||
|
|
||||||
|
if (reflectProperty.equals("Produced") && !originalProduced.isEmpty()) {
|
||||||
|
// check if a colorless shard can be paid from the trigger
|
||||||
|
if (toPay.equals(ManaCostShard.COLORLESS) && colorOrType.equals("Type") && originalProduced.contains("C")) {
|
||||||
|
manaProduced += " " + "C";
|
||||||
|
} else if (originalProduced.length() == 1) {
|
||||||
|
// if length is only one, and it either is equal C == Type
|
||||||
|
if (colorOrType.equals("Type") || !originalProduced.equals("C")) {
|
||||||
|
manaProduced += " " + originalProduced;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// should it look for other shards too?
|
||||||
|
boolean found = false;
|
||||||
|
for (String s : originalProduced.split(" ")) {
|
||||||
|
if (colorOrType.equals("Type") || !s.equals("C") && toPay.canBePaidWithManaOfColor(MagicColor.fromName(s))) {
|
||||||
|
found = true;
|
||||||
|
manaProduced += " " + s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no good mana found? just add the first generated color
|
||||||
|
if (!found) {
|
||||||
|
for (String s : originalProduced.split(" ")) {
|
||||||
|
if (colorOrType.equals("Type") || !s.equals("C")) {
|
||||||
|
manaProduced += " " + s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return manaProduced;
|
||||||
|
}
|
||||||
|
|
||||||
public static CardCollection getManaSourcesToPayCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) {
|
public static CardCollection getManaSourcesToPayCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) {
|
||||||
CardCollection manaSources = new CardCollection();
|
CardCollection manaSources = new CardCollection();
|
||||||
|
|
||||||
@@ -392,23 +578,13 @@ public class ComputerUtilMana {
|
|||||||
manaSources.add(saPayment.getHostCard());
|
manaSources.add(saPayment.getHostCard());
|
||||||
setExpressColorChoice(sa, ai, cost, toPay, saPayment);
|
setExpressColorChoice(sa, ai, cost, toPay, saPayment);
|
||||||
|
|
||||||
String manaProduced = toPay.isSnow() ? "S" : GameActionUtil.generatedMana(saPayment);
|
String manaProduced = predictManafromSpellAbility(saPayment, ai, toPay);
|
||||||
manaProduced = AbilityManaPart.applyManaReplacement(saPayment, manaProduced);
|
|
||||||
//System.out.println(manaProduced);
|
//System.out.println(manaProduced);
|
||||||
payMultipleMana(cost, manaProduced, ai);
|
payMultipleMana(cost, manaProduced, ai);
|
||||||
|
|
||||||
// remove from available lists
|
// remove from available lists
|
||||||
/*
|
Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
|
||||||
* Refactoring this code to sourcesForShards.values().removeIf((SpellAbility srcSa) -> srcSa.getHostCard().equals(saPayment.getHostCard()));
|
|
||||||
* causes Android build not to compile
|
|
||||||
* */
|
|
||||||
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
|
|
||||||
while (itSa.hasNext()) {
|
|
||||||
SpellAbility srcSa = itSa.next();
|
|
||||||
if (srcSa.getHostCard().equals(saPayment.getHostCard())) {
|
|
||||||
itSa.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOfferingsAI(sa, true, cost.isPaid());
|
handleOfferingsAI(sa, true, cost.isPaid());
|
||||||
@@ -443,6 +619,21 @@ public class ComputerUtilMana {
|
|||||||
|
|
||||||
// Loop over mana needed
|
// Loop over mana needed
|
||||||
while (!cost.isPaid()) {
|
while (!cost.isPaid()) {
|
||||||
|
while (!cost.isPaid() && !manapool.isEmpty()) {
|
||||||
|
boolean found = false;
|
||||||
|
for (byte color : MagicColor.WUBRGC) {
|
||||||
|
if (manapool.tryPayCostWithColor(color, sa, cost)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cost.isPaid()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
toPay = getNextShardToPay(cost);
|
toPay = getNextShardToPay(cost);
|
||||||
|
|
||||||
boolean lifeInsteadOfBlack = toPay.isBlack() && ai.hasKeyword("PayLifeInsteadOf:B");
|
boolean lifeInsteadOfBlack = toPay.isBlack() && ai.hasKeyword("PayLifeInsteadOf:B");
|
||||||
@@ -451,7 +642,8 @@ public class ComputerUtilMana {
|
|||||||
if (hasConverge &&
|
if (hasConverge &&
|
||||||
(toPay == ManaCostShard.GENERIC || toPay == ManaCostShard.X)) {
|
(toPay == ManaCostShard.GENERIC || toPay == ManaCostShard.X)) {
|
||||||
final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION;
|
final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION;
|
||||||
for (final byte b : ColorSet.fromMask(unpaidColors)) { // try and pay other colors for converge
|
for (final byte b : ColorSet.fromMask(unpaidColors)) {
|
||||||
|
// try and pay other colors for converge
|
||||||
final ManaCostShard shard = ManaCostShard.valueOf(b);
|
final ManaCostShard shard = ManaCostShard.valueOf(b);
|
||||||
saList = sourcesForShards.get(shard);
|
saList = sourcesForShards.get(shard);
|
||||||
if (saList != null && !saList.isEmpty()) {
|
if (saList != null && !saList.isEmpty()) {
|
||||||
@@ -459,7 +651,8 @@ public class ComputerUtilMana {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (saList == null || saList.isEmpty()) { // failed to converge, revert to paying generic
|
if (saList == null || saList.isEmpty()) {
|
||||||
|
// failed to converge, revert to paying generic
|
||||||
saList = sourcesForShards.get(toPay);
|
saList = sourcesForShards.get(toPay);
|
||||||
hasConverge = false;
|
hasConverge = false;
|
||||||
}
|
}
|
||||||
@@ -539,23 +732,13 @@ public class ComputerUtilMana {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String manaProduced = toPay.isSnow() ? "S" : GameActionUtil.generatedMana(saPayment);
|
String manaProduced = predictManafromSpellAbility(saPayment, ai, toPay);
|
||||||
manaProduced = AbilityManaPart.applyManaReplacement(saPayment, manaProduced);
|
|
||||||
//System.out.println(manaProduced);
|
// System.out.println(manaProduced);
|
||||||
payMultipleMana(cost, manaProduced, ai);
|
payMultipleMana(cost, manaProduced, ai);
|
||||||
|
|
||||||
// remove from available lists
|
// remove from available lists
|
||||||
/*
|
Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
|
||||||
* Refactoring this code to sourcesForShards.values().removeIf((SpellAbility srcSa) -> srcSa.getHostCard().equals(saPayment.getHostCard()));
|
|
||||||
* causes Android build not to compile
|
|
||||||
* */
|
|
||||||
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
|
|
||||||
while (itSa.hasNext()) {
|
|
||||||
SpellAbility srcSa = itSa.next();
|
|
||||||
if (srcSa.getHostCard().equals(saPayment.getHostCard())) {
|
|
||||||
itSa.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment);
|
final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment);
|
||||||
@@ -571,19 +754,9 @@ public class ComputerUtilMana {
|
|||||||
// no need to remove abilities from resource map,
|
// no need to remove abilities from resource map,
|
||||||
// once their costs are paid and consume resources, they can not be used again
|
// once their costs are paid and consume resources, they can not be used again
|
||||||
|
|
||||||
if (hasConverge) { // hack to prevent converge re-using sources
|
if (hasConverge) {
|
||||||
// remove from available lists
|
// hack to prevent converge re-using sources
|
||||||
/*
|
Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
|
||||||
* Refactoring this code to sourcesForShards.values().removeIf((SpellAbility srcSa) -> srcSa.getHostCard().equals(saPayment.getHostCard()));
|
|
||||||
* causes Android build not to compile
|
|
||||||
* */
|
|
||||||
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
|
|
||||||
while (itSa.hasNext()) {
|
|
||||||
SpellAbility srcSa = itSa.next();
|
|
||||||
if (srcSa.getHostCard().equals(saPayment.getHostCard())) {
|
|
||||||
itSa.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -596,15 +769,6 @@ public class ComputerUtilMana {
|
|||||||
// extraMana, sa.getHostCard(), sa.toUnsuppressedString(), StringUtils.join(paymentPlan, "\n\t"));
|
// extraMana, sa.getHostCard(), sa.toUnsuppressedString(), StringUtils.join(paymentPlan, "\n\t"));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// See if it's possible to pay with something that was left in the mana pool in corner cases,
|
|
||||||
// e.g. Gemstone Caverns with a Luck counter on it generating colored mana (which fails to be
|
|
||||||
// processed correctly on a per-ability basis, leaving floating mana in the pool)
|
|
||||||
if (!cost.isPaid() && !manapool.isEmpty()) {
|
|
||||||
for (byte color : MagicColor.WUBRGC) {
|
|
||||||
manapool.tryPayCostWithColor(color, sa, cost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The cost is still unpaid, so refund the mana and report
|
// The cost is still unpaid, so refund the mana and report
|
||||||
if (!cost.isPaid()) {
|
if (!cost.isPaid()) {
|
||||||
refundMana(manaSpentToPay, ai, sa);
|
refundMana(manaSpentToPay, ai, sa);
|
||||||
@@ -641,7 +805,8 @@ public class ComputerUtilMana {
|
|||||||
List<Mana> manaSpentToPay, final boolean hasConverge) {
|
List<Mana> manaSpentToPay, final boolean hasConverge) {
|
||||||
// arrange all mana abilities by color produced.
|
// arrange all mana abilities by color produced.
|
||||||
final ListMultimap<Integer, SpellAbility> manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, checkPlayable);
|
final ListMultimap<Integer, SpellAbility> manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, checkPlayable);
|
||||||
if (manaAbilityMap.isEmpty()) { // no mana abilities, bailing out
|
if (manaAbilityMap.isEmpty()) {
|
||||||
|
// no mana abilities, bailing out
|
||||||
refundMana(manaSpentToPay, ai, sa);
|
refundMana(manaSpentToPay, ai, sa);
|
||||||
handleOfferingsAI(sa, test, cost.isPaid());
|
handleOfferingsAI(sa, test, cost.isPaid());
|
||||||
return null;
|
return null;
|
||||||
@@ -652,14 +817,15 @@ public class ComputerUtilMana {
|
|||||||
|
|
||||||
// select which abilities may be used for each shard
|
// select which abilities may be used for each shard
|
||||||
ListMultimap<ManaCostShard, SpellAbility> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost);
|
ListMultimap<ManaCostShard, SpellAbility> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost);
|
||||||
if (hasConverge) { // add extra colors for paying converge
|
if (hasConverge) {
|
||||||
|
// add extra colors for paying converge
|
||||||
final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION;
|
final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION;
|
||||||
for (final byte b : ColorSet.fromMask(unpaidColors)) {
|
for (final byte b : ColorSet.fromMask(unpaidColors)) {
|
||||||
final ManaCostShard shard = ManaCostShard.valueOf(b);
|
final ManaCostShard shard = ManaCostShard.valueOf(b);
|
||||||
if (!sourcesForShards.containsKey(shard)) {
|
if (!sourcesForShards.containsKey(shard)) {
|
||||||
if (ai.getManaPool().canPayForShardWithColor(shard, b)) {
|
if (ai.getManaPool().canPayForShardWithColor(shard, b)) {
|
||||||
for (SpellAbility saMana : manaAbilityMap.get((int)b)) {
|
for (SpellAbility saMana : manaAbilityMap.get((int)b)) {
|
||||||
sourcesForShards.get(shard).add(sourcesForShards.get(shard).size(), saMana);
|
sourcesForShards.get(shard).add(saMana);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1026,7 +1192,7 @@ public class ComputerUtilMana {
|
|||||||
choice = abMana.getExpressChoice();
|
choice = abMana.getExpressChoice();
|
||||||
abMana.clearExpressChoice();
|
abMana.clearExpressChoice();
|
||||||
byte colorMask = ManaAtom.fromName(choice);
|
byte colorMask = ManaAtom.fromName(choice);
|
||||||
if (abMana.canProduce(choice, manaAb) && testCost.isAnyPartPayableWith(colorMask, ai.getManaPool())) {
|
if (manaAb.canProduce(choice) && testCost.isAnyPartPayableWith(colorMask, ai.getManaPool())) {
|
||||||
choiceString.append(choice);
|
choiceString.append(choice);
|
||||||
payMultipleMana(testCost, choice, ai);
|
payMultipleMana(testCost, choice, ai);
|
||||||
continue;
|
continue;
|
||||||
@@ -1387,20 +1553,6 @@ public class ComputerUtilMana {
|
|||||||
final ListMultimap<Integer, SpellAbility> manaMap = ArrayListMultimap.create();
|
final ListMultimap<Integer, SpellAbility> manaMap = ArrayListMultimap.create();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
List<ReplacementEffect> replacementEffects = new ArrayList<>();
|
|
||||||
for (final Player p : game.getPlayers()) {
|
|
||||||
for (final Card crd : p.getAllCards()) {
|
|
||||||
for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) {
|
|
||||||
if (replacementEffect.requirementsCheck(game)
|
|
||||||
&& replacementEffect.getMode() == ReplacementType.ProduceMana
|
|
||||||
&& replacementEffect.hasParam("ManaReplacement")
|
|
||||||
&& replacementEffect.zonesCheck(game.getZoneOf(crd))) {
|
|
||||||
replacementEffects.add(replacementEffect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop over all current available mana sources
|
// Loop over all current available mana sources
|
||||||
for (final Card sourceCard : getAvailableManaSources(ai, checkPlayable)) {
|
for (final Card sourceCard : getAvailableManaSources(ai, checkPlayable)) {
|
||||||
if (DEBUG_MANA_PAYMENT) {
|
if (DEBUG_MANA_PAYMENT) {
|
||||||
@@ -1430,48 +1582,80 @@ public class ComputerUtilMana {
|
|||||||
}
|
}
|
||||||
|
|
||||||
manaMap.get(ManaAtom.GENERIC).add(m); // add to generic source list
|
manaMap.get(ManaAtom.GENERIC).add(m); // add to generic source list
|
||||||
|
|
||||||
|
SpellAbility tail = m;
|
||||||
|
while (tail != null) {
|
||||||
AbilityManaPart mp = m.getManaPart();
|
AbilityManaPart mp = m.getManaPart();
|
||||||
|
if (mp != null && tail.metConditions()) {
|
||||||
|
// TODO Replacement Check currently doesn't work for reflected colors
|
||||||
|
|
||||||
// setup produce mana replacement effects
|
// setup produce mana replacement effects
|
||||||
|
String origin = mp.getOrigProduced();
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
|
||||||
repParams.put(AbilityKey.Mana, mp.getOrigProduced());
|
repParams.put(AbilityKey.Mana, origin);
|
||||||
repParams.put(AbilityKey.Affected, sourceCard);
|
repParams.put(AbilityKey.Affected, sourceCard);
|
||||||
repParams.put(AbilityKey.Player, ai);
|
repParams.put(AbilityKey.Player, ai);
|
||||||
repParams.put(AbilityKey.AbilityMana, m);
|
repParams.put(AbilityKey.AbilityMana, m); // RootAbility
|
||||||
|
|
||||||
for (final ReplacementEffect replacementEffect : replacementEffects) {
|
List<ReplacementEffect> reList = game.getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other);
|
||||||
if (replacementEffect.canReplace(repParams)) {
|
|
||||||
Card crd = replacementEffect.getHostCard();
|
|
||||||
String repType = crd.getSVar(replacementEffect.getParam("ManaReplacement"));
|
|
||||||
if (repType.contains("Chosen")) {
|
|
||||||
repType = TextUtil.fastReplace(repType, "Chosen", MagicColor.toShortString(crd.getChosenColor()));
|
|
||||||
}
|
|
||||||
mp.setManaReplaceType(repType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (reList.isEmpty()) {
|
||||||
Set<String> reflectedColors = CardUtil.getReflectableManaColors(m);
|
Set<String> reflectedColors = CardUtil.getReflectableManaColors(m);
|
||||||
// find possible colors
|
// find possible colors
|
||||||
if (mp.canProduce("W", m) || reflectedColors.contains(MagicColor.Constant.WHITE)) {
|
for (byte color : MagicColor.WUBRG) {
|
||||||
manaMap.get(ManaAtom.WHITE).add(m);
|
if (tail.canThisProduce(MagicColor.toShortString(color)) || reflectedColors.contains(MagicColor.toLongString(color))) {
|
||||||
|
manaMap.put((int)color, m);
|
||||||
}
|
}
|
||||||
if (mp.canProduce("U", m) || reflectedColors.contains(MagicColor.Constant.BLUE)) {
|
|
||||||
manaMap.get(ManaAtom.BLUE).add(m);
|
|
||||||
}
|
}
|
||||||
if (mp.canProduce("B", m) || reflectedColors.contains(MagicColor.Constant.BLACK)) {
|
if (m.canThisProduce("C") || reflectedColors.contains(MagicColor.Constant.COLORLESS)) {
|
||||||
manaMap.get(ManaAtom.BLACK).add(m);
|
manaMap.put(ManaAtom.COLORLESS, m);
|
||||||
}
|
}
|
||||||
if (mp.canProduce("R", m) || reflectedColors.contains(MagicColor.Constant.RED)) {
|
} else {
|
||||||
manaMap.get(ManaAtom.RED).add(m);
|
// try to guess the color the mana gets replaced to
|
||||||
|
for (ReplacementEffect re : reList) {
|
||||||
|
SpellAbility o = re.getOverridingAbility();
|
||||||
|
String replaced = origin;
|
||||||
|
if (o == null || o.getApi() != ApiType.ReplaceMana) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (mp.canProduce("G", m) || reflectedColors.contains(MagicColor.Constant.GREEN)) {
|
if (o.hasParam("ReplaceMana")) {
|
||||||
manaMap.get(ManaAtom.GREEN).add(m);
|
replaced = o.getParam("ReplaceMana");
|
||||||
|
} else if (o.hasParam("ReplaceType")) {
|
||||||
|
String color = o.getParam("ReplaceType");
|
||||||
|
for (byte c : MagicColor.WUBRGC) {
|
||||||
|
String s = MagicColor.toShortString(c);
|
||||||
|
replaced = replaced.replace(s, color);
|
||||||
}
|
}
|
||||||
if (mp.canProduce("C", m) || reflectedColors.contains(MagicColor.Constant.COLORLESS)) {
|
} else if (o.hasParam("ReplaceColor")) {
|
||||||
manaMap.get(ManaAtom.COLORLESS).add(m);
|
String color = o.getParam("ReplaceColor");
|
||||||
|
if (o.hasParam("ReplaceOnly")) {
|
||||||
|
replaced = replaced.replace(o.getParam("ReplaceOnly"), color);
|
||||||
|
} else {
|
||||||
|
for (byte c : MagicColor.WUBRG) {
|
||||||
|
String s = MagicColor.toShortString(c);
|
||||||
|
replaced = replaced.replace(s, color);
|
||||||
}
|
}
|
||||||
if (mp.isSnow()) {
|
}
|
||||||
manaMap.get(ManaAtom.IS_SNOW).add(m);
|
}
|
||||||
|
|
||||||
|
for (byte color : MagicColor.WUBRG) {
|
||||||
|
if ("Any".equals(replaced) || replaced.contains(MagicColor.toShortString(color))) {
|
||||||
|
manaMap.put((int)color, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (replaced.contains("C")) {
|
||||||
|
manaMap.put(ManaAtom.COLORLESS, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tail = tail.getSubAbility();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m.getHostCard().isSnow()) {
|
||||||
|
manaMap.put(ManaAtom.IS_SNOW, m);
|
||||||
}
|
}
|
||||||
if (DEBUG_MANA_PAYMENT) {
|
if (DEBUG_MANA_PAYMENT) {
|
||||||
System.out.println("DEBUG_MANA_PAYMENT: groupSourcesByManaColor manaMap = " + manaMap);
|
System.out.println("DEBUG_MANA_PAYMENT: groupSourcesByManaColor manaMap = " + manaMap);
|
||||||
|
|||||||
@@ -494,7 +494,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TargetChoices chooseNewTargetsFor(SpellAbility ability) {
|
public TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate<GameObject> filter, boolean optional) {
|
||||||
// AI currently can't do this. But when it can it will need to be based on Ability API
|
// AI currently can't do this. But when it can it will need to be based on Ability API
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -833,6 +833,9 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte chooseColor(String message, SpellAbility sa, ColorSet colors) {
|
public byte chooseColor(String message, SpellAbility sa, ColorSet colors) {
|
||||||
|
if (colors.countColors() < 2) {
|
||||||
|
return Iterables.getFirst(colors, MagicColor.WHITE);
|
||||||
|
}
|
||||||
// You may switch on sa.getApi() here and use sa.getParam("AILogic")
|
// You may switch on sa.getApi() here and use sa.getParam("AILogic")
|
||||||
CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
|
CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
|
||||||
if (sa.getApi() == ApiType.Mana) {
|
if (sa.getApi() == ApiType.Mana) {
|
||||||
@@ -978,9 +981,13 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
if (sa.isSpell()) {
|
if (sa.isSpell()) {
|
||||||
player.getGame().getStackZone().add(sa.getHostCard());
|
player.getGame().getStackZone().add(sa.getHostCard());
|
||||||
}
|
}
|
||||||
// TODO check if static abilities needs to be run for things affecting the copy?
|
|
||||||
|
/* FIXME: the new implementation (below) requires implementing setupNewTargets in the AI controller, among other possible changes, otherwise breaks AI
|
||||||
|
if (sa.isMayChooseNewTargets()) {
|
||||||
|
sa.setupNewTargets(player);
|
||||||
|
}
|
||||||
|
*/
|
||||||
if (sa.isMayChooseNewTargets() && !sa.setupTargets()) {
|
if (sa.isMayChooseNewTargets() && !sa.setupTargets()) {
|
||||||
// if targets can't be done, remove copy from existence
|
|
||||||
if (sa.isSpell()) {
|
if (sa.isSpell()) {
|
||||||
sa.getHostCard().ceaseToExist();
|
sa.getHostCard().ceaseToExist();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import forge.card.MagicColor;
|
|||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameType;
|
import forge.game.GameType;
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
@@ -636,10 +635,7 @@ public class SpecialCardAi {
|
|||||||
boolean canRetFromGrave = false;
|
boolean canRetFromGrave = false;
|
||||||
String name = c.getName().replace(',', ';');
|
String name = c.getName().replace(',', ';');
|
||||||
for (Trigger t : c.getTriggers()) {
|
for (Trigger t : c.getTriggers()) {
|
||||||
SpellAbility ab = null;
|
SpellAbility ab = t.ensureAbility();
|
||||||
if (t.hasParam("Execute")) {
|
|
||||||
ab = AbilityFactory.getAbility(c.getSVar(t.getParam("Execute")), c);
|
|
||||||
}
|
|
||||||
if (ab == null) { continue; }
|
if (ab == null) { continue; }
|
||||||
|
|
||||||
if (ab.getApi() == ApiType.ChangeZone
|
if (ab.getApi() == ApiType.ChangeZone
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public abstract class SpellAbilityAi {
|
|||||||
if (aiLogic.equals("CheckCondition")) {
|
if (aiLogic.equals("CheckCondition")) {
|
||||||
SpellAbility saCopy = sa.copy();
|
SpellAbility saCopy = sa.copy();
|
||||||
saCopy.setActivatingPlayer(ai);
|
saCopy.setActivatingPlayer(ai);
|
||||||
return saCopy.getConditions().areMet(saCopy);
|
return saCopy.metConditions();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !("Never".equals(aiLogic));
|
return !("Never".equals(aiLogic));
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ public class AnimateAi extends SpellAbilityAi {
|
|||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Game game = aiPlayer.getGame();
|
final Game game = aiPlayer.getGame();
|
||||||
final PhaseHandler ph = game.getPhaseHandler();
|
final PhaseHandler ph = game.getPhaseHandler();
|
||||||
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) {
|
if (!sa.metConditions() && sa.getSubAbility() == null) {
|
||||||
return false; // what is this for?
|
return false; // what is this for?
|
||||||
}
|
}
|
||||||
if (!game.getStack().isEmpty() && game.getStack().peekAbility().getApi() == ApiType.Sacrifice) {
|
if (!game.getStack().isEmpty() && game.getStack().peekAbility().getApi() == ApiType.Sacrifice) {
|
||||||
|
|||||||
@@ -1086,12 +1086,7 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
final Map<String, String> params = t.getMapParams();
|
final Map<String, String> params = t.getMapParams();
|
||||||
if ("Card.Self".equals(params.get("ValidCard"))
|
if ("Card.Self".equals(params.get("ValidCard"))
|
||||||
&& "Battlefield".equals(params.get("Destination"))) {
|
&& "Battlefield".equals(params.get("Destination"))) {
|
||||||
SpellAbility trigSa = null;
|
SpellAbility trigSa = t.ensureAbility();
|
||||||
if (t.hasParam("Execute") && attachSource.hasSVar(t.getParam("Execute"))) {
|
|
||||||
trigSa = AbilityFactory.getAbility(attachSource.getSVar(params.get("Execute")), attachSource);
|
|
||||||
} else if (t.getOverridingAbility() != null) {
|
|
||||||
trigSa = t.getOverridingAbility();
|
|
||||||
}
|
|
||||||
if (trigSa != null && trigSa.getApi() == ApiType.DealDamage && "Enchanted".equals(trigSa.getParam("Defined"))) {
|
if (trigSa != null && trigSa.getApi() == ApiType.DealDamage && "Enchanted".equals(trigSa.getParam("Defined"))) {
|
||||||
for (Card target : list) {
|
for (Card target : list) {
|
||||||
if (!target.getController().isOpponentOf(ai)) {
|
if (!target.getController().isOpponentOf(ai)) {
|
||||||
|
|||||||
@@ -304,10 +304,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// don't play if the conditions aren't met, unless it would trigger a beneficial sub-condition
|
// don't play if the conditions aren't met, unless it would trigger a beneficial sub-condition
|
||||||
if (!activateForCost && !sa.getConditions().areMet(sa)) {
|
if (!activateForCost && !sa.metConditions()) {
|
||||||
final AbilitySub abSub = sa.getSubAbility();
|
final AbilitySub abSub = sa.getSubAbility();
|
||||||
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
|
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
|
||||||
if (!abSub.getConditions().areMet(abSub)) {
|
if (!abSub.metConditions()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
Card choice = null;
|
Card choice = null;
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
final boolean divided = sa.isDividedAsYouChoose();
|
||||||
final String logic = sa.getParamOrDefault("AILogic", "");
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
PhaseHandler ph = ai.getGame().getPhaseHandler();
|
||||||
|
|
||||||
@@ -290,7 +290,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
return doMoveCounterLogic(ai, sa, ph);
|
return doMoveCounterLogic(ai, sa, ph);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) {
|
if (!sa.metConditions() && sa.getSubAbility() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,16 +398,15 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa)) {
|
if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
|
||||||
// only evaluates case where all tokens are placed on a single target
|
// only evaluates case where all tokens are placed on a single target
|
||||||
if (sa.usesTargeting() && abTgt.getMinTargets(source, sa) < 2) {
|
if (sa.usesTargeting() && sa.getMinTargets() < 2) {
|
||||||
if (ComputerUtilCard.canPumpAgainstRemoval(ai, sa)) {
|
if (ComputerUtilCard.canPumpAgainstRemoval(ai, sa)) {
|
||||||
Card c = sa.getTargets().getFirstTargetedCard();
|
Card c = sa.getTargets().getFirstTargetedCard();
|
||||||
if (sa.getTargets().size() > 1) {
|
if (sa.getTargets().size() > 1) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
}
|
}
|
||||||
abTgt.addDividedAllocation(sa.getTargetCard(), amount);
|
sa.addDividedAllocation(sa.getTargetCard(), amount);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -462,7 +461,6 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sourceName.equals("Abzan Charm")) {
|
if (sourceName.equals("Abzan Charm")) {
|
||||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
|
||||||
// specific AI for instant with distribute two +1/+1 counters
|
// specific AI for instant with distribute two +1/+1 counters
|
||||||
ComputerUtilCard.sortByEvaluateCreature(list);
|
ComputerUtilCard.sortByEvaluateCreature(list);
|
||||||
// maximise the number of targets
|
// maximise the number of targets
|
||||||
@@ -472,11 +470,11 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, i, i,
|
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, i, i,
|
||||||
Lists.newArrayList())) {
|
Lists.newArrayList())) {
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
abTgt.addDividedAllocation(c, i);
|
sa.addDividedAllocation(c, i);
|
||||||
left -= i;
|
left -= i;
|
||||||
}
|
}
|
||||||
if (left < i || sa.getTargets().size() == abTgt.getMaxTargets(source, sa)) {
|
if (left < i || sa.getTargets().size() == sa.getMaxTargets()) {
|
||||||
abTgt.addDividedAllocation(sa.getTargets().getFirstTargetedCard(), left + i);
|
sa.addDividedAllocation(sa.getTargets().getFirstTargetedCard(), left + i);
|
||||||
left = 0;
|
left = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -542,7 +540,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
list.remove(choice);
|
list.remove(choice);
|
||||||
sa.getTargets().add(choice);
|
sa.getTargets().add(choice);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
sa.getTargetRestrictions().addDividedAllocation(choice, amount);
|
sa.addDividedAllocation(choice, amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
choice = null;
|
choice = null;
|
||||||
@@ -609,7 +607,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
final String logic = sa.getParamOrDefault("AILogic", "");
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
final boolean divided = sa.isDividedAsYouChoose();
|
||||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
|
|
||||||
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
|
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
|
||||||
@@ -672,7 +670,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
list.remove(choice);
|
list.remove(choice);
|
||||||
sa.getTargets().add(choice);
|
sa.getTargets().add(choice);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
sa.getTargetRestrictions().addDividedAllocation(choice, amount);
|
sa.addDividedAllocation(choice, amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -690,7 +688,7 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
CardCollection list;
|
CardCollection list;
|
||||||
final String type = sa.getParam("CounterType");
|
final String type = sa.getParam("CounterType");
|
||||||
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
final boolean divided = sa.isDividedAsYouChoose();
|
||||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||||
int left = amount;
|
int left = amount;
|
||||||
|
|
||||||
@@ -818,12 +816,11 @@ public class CountersPutAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (choice != null && divided) {
|
if (choice != null && divided) {
|
||||||
final TargetRestrictions abTgt = sa.getTargetRestrictions();
|
|
||||||
int alloc = Math.max(amount / totalTargets, 1);
|
int alloc = Math.max(amount / totalTargets, 1);
|
||||||
if (sa.getTargets().size() == Math.min(totalTargets, abTgt.getMaxTargets(sa.getHostCard(), sa)) - 1) {
|
if (sa.getTargets().size() == Math.min(totalTargets, sa.getMaxTargets()) - 1) {
|
||||||
abTgt.addDividedAllocation(choice, left);
|
sa.addDividedAllocation(choice, left);
|
||||||
} else {
|
} else {
|
||||||
abTgt.addDividedAllocation(choice, alloc);
|
sa.addDividedAllocation(choice, alloc);
|
||||||
left -= alloc;
|
left -= alloc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if ((damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) ||
|
if ((damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) ||
|
||||||
sourceName.equals("Crater's Claws")){
|
sourceName.equals("Crater's Claws")){
|
||||||
// If I can kill my target by paying less mana, do it
|
// If I can kill my target by paying less mana, do it
|
||||||
if (sa.usesTargeting() && !sa.getTargets().isTargetingAnyPlayer() && !sa.hasParam("DividedAsYouChoose")) {
|
if (sa.usesTargeting() && !sa.getTargets().isTargetingAnyPlayer() && !sa.isDividedAsYouChoose()) {
|
||||||
int actualPay = dmg;
|
int actualPay = dmg;
|
||||||
final boolean noPrevention = sa.hasParam("NoPrevention");
|
final boolean noPrevention = sa.hasParam("NoPrevention");
|
||||||
for (final Card c : sa.getTargets().getTargetCards()) {
|
for (final Card c : sa.getTargets().getTargetCards()) {
|
||||||
@@ -547,7 +547,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
final boolean noPrevention = sa.hasParam("NoPrevention");
|
final boolean noPrevention = sa.hasParam("NoPrevention");
|
||||||
final Game game = source.getGame();
|
final Game game = source.getGame();
|
||||||
final PhaseHandler phase = game.getPhaseHandler();
|
final PhaseHandler phase = game.getPhaseHandler();
|
||||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
final boolean divided = sa.isDividedAsYouChoose();
|
||||||
final boolean oppTargetsChoice = sa.hasParam("TargetingPlayer");
|
final boolean oppTargetsChoice = sa.hasParam("TargetingPlayer");
|
||||||
final String logic = sa.getParamOrDefault("AILogic", "");
|
final String logic = sa.getParamOrDefault("AILogic", "");
|
||||||
|
|
||||||
@@ -613,7 +613,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (assignedDamage <= dmg
|
if (assignedDamage <= dmg
|
||||||
&& humanCreature.getShieldCount() == 0 && !ComputerUtil.canRegenerate(humanCreature.getController(), humanCreature)) {
|
&& humanCreature.getShieldCount() == 0 && !ComputerUtil.canRegenerate(humanCreature.getController(), humanCreature)) {
|
||||||
tcs.add(humanCreature);
|
tcs.add(humanCreature);
|
||||||
tgt.addDividedAllocation(humanCreature, assignedDamage);
|
sa.addDividedAllocation(humanCreature, assignedDamage);
|
||||||
lastTgt = humanCreature;
|
lastTgt = humanCreature;
|
||||||
dmg -= assignedDamage;
|
dmg -= assignedDamage;
|
||||||
}
|
}
|
||||||
@@ -625,7 +625,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dmg > 0 && lastTgt != null) {
|
if (dmg > 0 && lastTgt != null) {
|
||||||
tgt.addDividedAllocation(lastTgt, tgt.getDividedValue(lastTgt) + dmg);
|
sa.addDividedAllocation(lastTgt, sa.getDividedValue(lastTgt) + dmg);
|
||||||
dmg = 0;
|
dmg = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -635,14 +635,14 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
tcs.add(humanCreature);
|
tcs.add(humanCreature);
|
||||||
tgt.addDividedAllocation(humanCreature, dmg);
|
sa.addDividedAllocation(humanCreature, dmg);
|
||||||
dmg = 0;
|
dmg = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalTargetedSoFar = -1;
|
int totalTargetedSoFar = -1;
|
||||||
while (tcs.size() < tgt.getMaxTargets(source, sa)) {
|
while (sa.canAddMoreTarget()) {
|
||||||
if (totalTargetedSoFar == tcs.size()) {
|
if (totalTargetedSoFar == tcs.size()) {
|
||||||
// Avoid looping endlessly when choosing targets for cards with variable target number and type
|
// Avoid looping endlessly when choosing targets for cards with variable target number and type
|
||||||
// like Jaya's Immolating Inferno
|
// like Jaya's Immolating Inferno
|
||||||
@@ -664,7 +664,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (divided) {
|
if (divided) {
|
||||||
int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
|
int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
|
||||||
assignedDamage = Math.min(dmg, assignedDamage);
|
assignedDamage = Math.min(dmg, assignedDamage);
|
||||||
tgt.addDividedAllocation(c, assignedDamage);
|
sa.addDividedAllocation(c, assignedDamage);
|
||||||
dmg = dmg - assignedDamage;
|
dmg = dmg - assignedDamage;
|
||||||
if (dmg <= 0) {
|
if (dmg <= 0) {
|
||||||
break;
|
break;
|
||||||
@@ -680,7 +680,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (this.shouldTgtP(ai, sa, dmg, noPrevention)) {
|
if (this.shouldTgtP(ai, sa, dmg, noPrevention)) {
|
||||||
tcs.add(enemy);
|
tcs.add(enemy);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgt.addDividedAllocation(enemy, dmg);
|
sa.addDividedAllocation(enemy, dmg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -702,7 +702,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (divided) {
|
if (divided) {
|
||||||
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
|
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
|
||||||
if (assignedDamage <= dmg) {
|
if (assignedDamage <= dmg) {
|
||||||
tgt.addDividedAllocation(c, assignedDamage);
|
sa.addDividedAllocation(c, assignedDamage);
|
||||||
}
|
}
|
||||||
dmg = dmg - assignedDamage;
|
dmg = dmg - assignedDamage;
|
||||||
if (dmg <= 0) {
|
if (dmg <= 0) {
|
||||||
@@ -739,7 +739,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (freePing && sa.canTarget(enemy) && (!avoidTargetP(ai, sa))) {
|
if (freePing && sa.canTarget(enemy) && (!avoidTargetP(ai, sa))) {
|
||||||
tcs.add(enemy);
|
tcs.add(enemy);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgt.addDividedAllocation(enemy, dmg);
|
sa.addDividedAllocation(enemy, dmg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -757,9 +757,9 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (divided) {
|
if (divided) {
|
||||||
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
|
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
|
||||||
if (assignedDamage <= dmg) {
|
if (assignedDamage <= dmg) {
|
||||||
tgt.addDividedAllocation(c, assignedDamage);
|
sa.addDividedAllocation(c, assignedDamage);
|
||||||
} else {
|
} else {
|
||||||
tgt.addDividedAllocation(c, dmg);
|
sa.addDividedAllocation(c, dmg);
|
||||||
}
|
}
|
||||||
dmg = dmg - assignedDamage;
|
dmg = dmg - assignedDamage;
|
||||||
if (dmg <= 0) {
|
if (dmg <= 0) {
|
||||||
@@ -785,7 +785,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
(!avoidTargetP(ai, sa))) {
|
(!avoidTargetP(ai, sa))) {
|
||||||
tcs.add(enemy);
|
tcs.add(enemy);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgt.addDividedAllocation(enemy, dmg);
|
sa.addDividedAllocation(enemy, dmg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -880,16 +880,16 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
private boolean damageChooseRequiredTargets(final Player ai, final SpellAbility sa, final TargetRestrictions tgt, final int dmg) {
|
private boolean damageChooseRequiredTargets(final Player ai, final SpellAbility sa, final TargetRestrictions tgt, final int dmg) {
|
||||||
// this is for Triggered targets that are mandatory
|
// this is for Triggered targets that are mandatory
|
||||||
final boolean noPrevention = sa.hasParam("NoPrevention");
|
final boolean noPrevention = sa.hasParam("NoPrevention");
|
||||||
final boolean divided = sa.hasParam("DividedAsYouChoose");
|
final boolean divided = sa.isDividedAsYouChoose();
|
||||||
final Player opp = ai.getWeakestOpponent();
|
final Player opp = ai.getWeakestOpponent();
|
||||||
|
|
||||||
while (sa.getTargets().size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
|
while (sa.canAddMoreTarget()) {
|
||||||
if (tgt.canTgtPlaneswalker()) {
|
if (tgt.canTgtPlaneswalker()) {
|
||||||
final Card c = this.dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, ai, true);
|
final Card c = this.dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, ai, true);
|
||||||
if (c != null) {
|
if (c != null) {
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgt.addDividedAllocation(c, dmg);
|
sa.addDividedAllocation(c, dmg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -902,7 +902,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (c != null) {
|
if (c != null) {
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgt.addDividedAllocation(c, dmg);
|
sa.addDividedAllocation(c, dmg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -912,7 +912,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (sa.canTarget(opp)) {
|
if (sa.canTarget(opp)) {
|
||||||
if (sa.getTargets().add(opp)) {
|
if (sa.getTargets().add(opp)) {
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgt.addDividedAllocation(opp, dmg);
|
sa.addDividedAllocation(opp, dmg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -927,7 +927,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
Card c = ComputerUtilCard.getWorstPermanentAI(indestructible, false, false, false, false);
|
Card c = ComputerUtilCard.getWorstPermanentAI(indestructible, false, false, false, false);
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgt.addDividedAllocation(c, dmg);
|
sa.addDividedAllocation(c, dmg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -938,7 +938,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (c != null) {
|
if (c != null) {
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgt.addDividedAllocation(c, dmg);
|
sa.addDividedAllocation(c, dmg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -948,7 +948,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (sa.canTarget(ai)) {
|
if (sa.canTarget(ai)) {
|
||||||
if (sa.getTargets().add(ai)) {
|
if (sa.getTargets().add(ai)) {
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgt.addDividedAllocation(ai, dmg);
|
sa.addDividedAllocation(ai, dmg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -988,7 +988,7 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid") && !sa.hasParam("DividedAsYouChoose")) {
|
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid") && !sa.isDividedAsYouChoose()) {
|
||||||
// If I can kill my target by paying less mana, do it
|
// If I can kill my target by paying less mana, do it
|
||||||
int actualPay = 0;
|
int actualPay = 0;
|
||||||
final boolean noPrevention = sa.hasParam("NoPrevention");
|
final boolean noPrevention = sa.hasParam("NoPrevention");
|
||||||
|
|||||||
@@ -146,8 +146,8 @@ public class DamagePreventAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tgt != null && sa.hasParam("DividedAsYouChoose") && sa.getTargets() != null && !sa.getTargets().isEmpty()) {
|
if (sa.usesTargeting() && sa.isDividedAsYouChoose() && !sa.getTargets().isEmpty()) {
|
||||||
tgt.addDividedAllocation(sa.getTargets().get(0), AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
|
sa.addDividedAllocation(sa.getTargets().get(0), AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
return chance;
|
return chance;
|
||||||
@@ -179,12 +179,11 @@ public class DamagePreventAi extends SpellAbilityAi {
|
|||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
private boolean preventDamageMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
private boolean preventDamageMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
// filter AIs battlefield by what I can target
|
// filter AIs battlefield by what I can target
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
CardCollectionView targetables = game.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView targetables = game.getCardsIn(ZoneType.Battlefield);
|
||||||
targetables = CardLists.getValidCards(targetables, tgt.getValidTgts(), ai, sa.getHostCard(), sa);
|
targetables = CardLists.getTargetableCards(targetables, sa);
|
||||||
final List<Card> compTargetables = CardLists.filterControlledBy(targetables, ai);
|
final List<Card> compTargetables = CardLists.filterControlledBy(targetables, ai);
|
||||||
Card target = null;
|
Card target = null;
|
||||||
|
|
||||||
@@ -215,8 +214,8 @@ public class DamagePreventAi extends SpellAbilityAi {
|
|||||||
target = ComputerUtilCard.getCheapestPermanentAI(targetables, sa, true);
|
target = ComputerUtilCard.getCheapestPermanentAI(targetables, sa, true);
|
||||||
}
|
}
|
||||||
sa.getTargets().add(target);
|
sa.getTargets().add(target);
|
||||||
if (sa.hasParam("DividedAsYouChoose")) {
|
if (sa.isDividedAsYouChoose()) {
|
||||||
tgt.addDividedAllocation(target, AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
|
sa.addDividedAllocation(target, AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package forge.ai.ability;
|
|||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
@@ -22,11 +21,9 @@ public class DelayedTriggerAi extends SpellAbilityAi {
|
|||||||
// TODO: improve ai
|
// TODO: improve ai
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
SpellAbility trigsa = null;
|
SpellAbility trigsa = sa.getAdditionalAbility("Execute");
|
||||||
if (sa.hasAdditionalAbility("Execute")) {
|
if (trigsa == null) {
|
||||||
trigsa = sa.getAdditionalAbility("Execute");
|
return false;
|
||||||
} else {
|
|
||||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
|
||||||
}
|
}
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
@@ -39,12 +36,11 @@ public class DelayedTriggerAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||||
SpellAbility trigsa = null;
|
SpellAbility trigsa = sa.getAdditionalAbility("Execute");
|
||||||
if (sa.hasAdditionalAbility("Execute")) {
|
if (trigsa == null) {
|
||||||
trigsa = sa.getAdditionalAbility("Execute");
|
return false;
|
||||||
} else {
|
|
||||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
@@ -143,11 +139,9 @@ public class DelayedTriggerAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generic logic
|
// Generic logic
|
||||||
SpellAbility trigsa = null;
|
SpellAbility trigsa = sa.getAdditionalAbility("Execute");
|
||||||
if (sa.hasAdditionalAbility("Execute")) {
|
if (trigsa == null) {
|
||||||
trigsa = sa.getAdditionalAbility("Execute");
|
return false;
|
||||||
} else {
|
|
||||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
|
||||||
}
|
}
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package forge.ai.ability;
|
|||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.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;
|
||||||
@@ -253,17 +254,21 @@ public class FightAi extends SpellAbilityAi {
|
|||||||
private static int getSpellBonus(final Card aiCreature) {
|
private static int getSpellBonus(final Card aiCreature) {
|
||||||
for (Trigger t : aiCreature.getTriggers()) {
|
for (Trigger t : aiCreature.getTriggers()) {
|
||||||
if (t.getMode() == TriggerType.SpellCast) {
|
if (t.getMode() == TriggerType.SpellCast) {
|
||||||
|
SpellAbility sa = t.ensureAbility();
|
||||||
final Map<String, String> params = t.getMapParams();
|
final Map<String, String> params = t.getMapParams();
|
||||||
if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer"))
|
if (sa == null) {
|
||||||
&& params.containsKey("Execute")) {
|
continue;
|
||||||
|
}
|
||||||
|
if (ApiType.PutCounter.equals(sa.getApi())) {
|
||||||
|
if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer"))) {
|
||||||
SpellAbility heroic = AbilityFactory.getAbility(aiCreature.getSVar(params.get("Execute")),aiCreature);
|
SpellAbility heroic = AbilityFactory.getAbility(aiCreature.getSVar(params.get("Execute")),aiCreature);
|
||||||
if ("Self".equals(heroic.getParam("Defined")) && "P1P1".equals(heroic.getParam("CounterType"))) {
|
if ("Self".equals(heroic.getParam("Defined")) && "P1P1".equals(heroic.getParam("CounterType"))) {
|
||||||
return AbilityUtils.calculateAmount(aiCreature, heroic.getParam("CounterNum"), heroic);
|
return AbilityUtils.calculateAmount(aiCreature, heroic.getParam("CounterNum"), heroic);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ("ProwessPump".equals(params.get("Execute"))) {
|
} else if (ApiType.Pump.equals(sa.getApi())) {
|
||||||
return 1;
|
// TODO add prowess boost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import forge.ai.*;
|
import forge.ai.*;
|
||||||
import forge.game.ability.AbilityFactory;
|
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -16,12 +15,11 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpellAbility trigsa = null;
|
SpellAbility trigsa = sa.getAdditionalAbility("Execute");
|
||||||
if (sa.hasAdditionalAbility("Execute")) {
|
if (trigsa == null) {
|
||||||
trigsa = sa.getAdditionalAbility("Execute");
|
return false;
|
||||||
} else {
|
|
||||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
if (trigsa instanceof AbilitySub) {
|
if (trigsa instanceof AbilitySub) {
|
||||||
@@ -33,12 +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) {
|
||||||
SpellAbility trigsa = null;
|
SpellAbility trigsa = sa.getAdditionalAbility("Execute");
|
||||||
if (sa.hasAdditionalAbility("Execute")) {
|
if (trigsa == null) {
|
||||||
trigsa = sa.getAdditionalAbility("Execute");
|
return false;
|
||||||
} else {
|
|
||||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
|
|
||||||
@@ -56,12 +53,11 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpellAbility trigsa = null;
|
SpellAbility trigsa = sa.getAdditionalAbility("Execute");
|
||||||
if (sa.hasAdditionalAbility("Execute")) {
|
if (trigsa == null) {
|
||||||
trigsa = sa.getAdditionalAbility("Execute");
|
return false;
|
||||||
} else {
|
|
||||||
trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trigsa.setActivatingPlayer(ai);
|
trigsa.setActivatingPlayer(ai);
|
||||||
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ public class LifeGainAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
// don't play if the conditions aren't met, unless it would trigger a
|
// don't play if the conditions aren't met, unless it would trigger a
|
||||||
// beneficial sub-condition
|
// beneficial sub-condition
|
||||||
if (!activateForCost && !sa.getConditions().areMet(sa)) {
|
if (!activateForCost && !sa.metConditions()) {
|
||||||
final AbilitySub abSub = sa.getSubAbility();
|
final AbilitySub abSub = sa.getSubAbility();
|
||||||
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
|
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
|
||||||
if (!abSub.getConditions().areMet(abSub)) {
|
if (!abSub.getConditions().areMet(abSub)) {
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ public class PermanentAi extends SpellAbilityAi {
|
|||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
|
|
||||||
if (sa.getConditions() != null && !sa.getConditions().areMet(sa)) {
|
if (!sa.metConditions()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.google.common.collect.Lists;
|
|||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import forge.LobbyPlayer;
|
import forge.LobbyPlayer;
|
||||||
import forge.ai.LobbyPlayerAi;
|
import forge.ai.LobbyPlayerAi;
|
||||||
import forge.card.CardStateName;
|
|
||||||
import forge.game.*;
|
import forge.game.*;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.card.token.TokenInfo;
|
import forge.game.card.token.TokenInfo;
|
||||||
@@ -289,16 +288,9 @@ public class GameCopier {
|
|||||||
newCard.setTapped(true);
|
newCard.setTapped(true);
|
||||||
}
|
}
|
||||||
if (c.isFaceDown()) {
|
if (c.isFaceDown()) {
|
||||||
boolean isCreature = newCard.isCreature();
|
|
||||||
boolean hasManaCost = !newCard.getManaCost().isNoCost();
|
|
||||||
newCard.turnFaceDown(true);
|
newCard.turnFaceDown(true);
|
||||||
if (c.isManifested()) {
|
if (c.isManifested()) {
|
||||||
newCard.setManifested(true);
|
newCard.setManifested(true);
|
||||||
// TODO: Should be able to copy other abilities...
|
|
||||||
if (isCreature && hasManaCost) {
|
|
||||||
newCard.getState(CardStateName.Original).addSpellAbility(
|
|
||||||
CardFactoryUtil.abilityManifestFaceUp(newCard, newCard.getManaCost()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c.isMonstrous()) {
|
if (c.isMonstrous()) {
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import forge.game.phase.PhaseType;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetChoices;
|
import forge.game.spellability.TargetChoices;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
|
||||||
import forge.util.TextUtil;
|
|
||||||
|
|
||||||
public class GameSimulator {
|
public class GameSimulator {
|
||||||
public static boolean COPY_STACK = false;
|
public static boolean COPY_STACK = false;
|
||||||
@@ -125,11 +123,13 @@ public class GameSimulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SpellAbility findSaInSimGame(SpellAbility sa) {
|
private SpellAbility findSaInSimGame(SpellAbility sa) {
|
||||||
|
// is already an ability from sim game
|
||||||
|
if (sa.getHostCard().getGame().equals(this.simGame)) {
|
||||||
|
return sa;
|
||||||
|
}
|
||||||
Card origHostCard = sa.getHostCard();
|
Card origHostCard = sa.getHostCard();
|
||||||
Card hostCard = (Card) copier.find(origHostCard);
|
Card hostCard = (Card) copier.find(origHostCard);
|
||||||
String desc = sa.getDescription();
|
String desc = sa.getDescription();
|
||||||
// FIXME: This is a hack that makes testManifest pass - figure out why it's needed.
|
|
||||||
desc = TextUtil.fastReplace(desc, "Unmanifest {0}", "Unmanifest no cost");
|
|
||||||
for (SpellAbility cSa : hostCard.getSpellAbilities()) {
|
for (SpellAbility cSa : hostCard.getSpellAbilities()) {
|
||||||
if (desc.startsWith(cSa.getDescription())) {
|
if (desc.startsWith(cSa.getDescription())) {
|
||||||
return cSa;
|
return cSa;
|
||||||
@@ -163,14 +163,12 @@ public class GameSimulator {
|
|||||||
SpellAbility saOrSubSa = sa;
|
SpellAbility saOrSubSa = sa;
|
||||||
do {
|
do {
|
||||||
if (origSaOrSubSa.usesTargeting()) {
|
if (origSaOrSubSa.usesTargeting()) {
|
||||||
final boolean divided = origSaOrSubSa.hasParam("DividedAsYouChoose");
|
final boolean divided = origSaOrSubSa.isDividedAsYouChoose();
|
||||||
final TargetRestrictions origTgtRes = origSaOrSubSa.getTargetRestrictions();
|
|
||||||
final TargetRestrictions tgtRes = saOrSubSa.getTargetRestrictions();
|
|
||||||
for (final GameObject o : origSaOrSubSa.getTargets()) {
|
for (final GameObject o : origSaOrSubSa.getTargets()) {
|
||||||
final GameObject target = copier.find(o);
|
final GameObject target = copier.find(o);
|
||||||
saOrSubSa.getTargets().add(target);
|
saOrSubSa.getTargets().add(target);
|
||||||
if (divided) {
|
if (divided) {
|
||||||
tgtRes.addDividedAllocation(target, origTgtRes.getDividedValue(o));
|
saOrSubSa.addDividedAllocation(target, origSaOrSubSa.getDividedValue(o));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,16 +173,15 @@ public class PossibleTargetSelector {
|
|||||||
|
|
||||||
// Divide up counters, since AI is expected to do this. For now,
|
// Divide up counters, since AI is expected to do this. For now,
|
||||||
// divided evenly with left-overs going to the first target.
|
// divided evenly with left-overs going to the first target.
|
||||||
if (targetingSa.hasParam("DividedAsYouChoose")) {
|
if (targetingSa.isDividedAsYouChoose()) {
|
||||||
final int targetCount = targetingSa.getTargets().getTargetCards().size();
|
final int targetCount = targetingSa.getTargets().getTargetCards().size();
|
||||||
if (targetCount > 0) {
|
if (targetCount > 0) {
|
||||||
final String amountStr = targetingSa.getParam("CounterNum");
|
final String amountStr = targetingSa.getParam("CounterNum");
|
||||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, targetingSa);
|
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, targetingSa);
|
||||||
final int amountPerCard = amount / targetCount;
|
final int amountPerCard = amount / targetCount;
|
||||||
int amountLeftOver = amount - (amountPerCard * targetCount);
|
int amountLeftOver = amount - (amountPerCard * targetCount);
|
||||||
final TargetRestrictions tgtRes = targetingSa.getTargetRestrictions();
|
|
||||||
for (GameObject target : targetingSa.getTargets()) {
|
for (GameObject target : targetingSa.getTargets()) {
|
||||||
tgtRes.addDividedAllocation(target, amountPerCard + amountLeftOver);
|
targetingSa.addDividedAllocation(target, amountPerCard + amountLeftOver);
|
||||||
amountLeftOver = 0;
|
amountLeftOver = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -286,4 +286,11 @@ public final class FileUtil {
|
|||||||
}, 5000); //abort reading file if it takes longer than 5 seconds
|
}, 5000); //abort reading file if it takes longer than 5 seconds
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getParent(final String resourcePath) {
|
||||||
|
File f = new File(resourcePath);
|
||||||
|
if (f.getParentFile().getName() != null)
|
||||||
|
return f.getParentFile().getName();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,19 @@ package forge.game;
|
|||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
|
||||||
|
import forge.game.card.Card;
|
||||||
|
|
||||||
public class CardTraitPredicates {
|
public class CardTraitPredicates {
|
||||||
|
|
||||||
|
public static final Predicate<CardTraitBase> isHostCard(final Card host) {
|
||||||
|
return new Predicate<CardTraitBase>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(final CardTraitBase sa) {
|
||||||
|
return host.equals(sa.getHostCard());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static final Predicate<CardTraitBase> hasParam(final String name) {
|
public static final Predicate<CardTraitBase> hasParam(final String name) {
|
||||||
return new Predicate<CardTraitBase>() {
|
return new Predicate<CardTraitBase>() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import forge.card.MagicColor;
|
|||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardState;
|
import forge.game.card.CardState;
|
||||||
|
import forge.game.cost.Cost;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
@@ -131,6 +132,9 @@ public class ForgeScript {
|
|||||||
return !sa.isManaAbility();
|
return !sa.isManaAbility();
|
||||||
} else if (property.equals("withoutXCost")) {
|
} else if (property.equals("withoutXCost")) {
|
||||||
return !sa.costHasManaX();
|
return !sa.costHasManaX();
|
||||||
|
} else if (property.equals("hasTapCost")) {
|
||||||
|
Cost cost = sa.getPayCosts();
|
||||||
|
return cost != null && cost.hasTapCost();
|
||||||
} else if (property.equals("Buyback")) {
|
} else if (property.equals("Buyback")) {
|
||||||
return sa.isBuyBackAbility();
|
return sa.isBuyBackAbility();
|
||||||
} else if (property.equals("Cycling")) {
|
} else if (property.equals("Cycling")) {
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ public class GameAction {
|
|||||||
if (!c.isRealToken()) {
|
if (!c.isRealToken()) {
|
||||||
copied = CardFactory.copyCard(c, false);
|
copied = CardFactory.copyCard(c, false);
|
||||||
|
|
||||||
if (fromBattlefield) {
|
if (!zoneTo.is(ZoneType.Stack)) {
|
||||||
// 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)
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game;
|
package forge.game;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
@@ -252,6 +251,18 @@ public final class GameActionUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sa.isManaAbility() && sa.isActivatedAbility() && activator.hasKeyword("Piracy") && source.isLand() && source.isInPlay() && !activator.equals(source.getController()) && sa.getPayCosts().hasTapCost()) {
|
||||||
|
SpellAbility newSA = sa.copy(activator);
|
||||||
|
// to bypass Activator restriction, set Activator to Player
|
||||||
|
sa.getRestrictions().setActivator("Player");
|
||||||
|
|
||||||
|
// extra Mana restriction to only Spells
|
||||||
|
for (AbilityManaPart mp : newSA.getAllManaParts()) {
|
||||||
|
mp.setExtraManaRestriction("Spell");
|
||||||
|
}
|
||||||
|
alternatives.add(newSA);
|
||||||
|
}
|
||||||
|
|
||||||
// below are for some special cases of activated abilities
|
// below are for some special cases of activated abilities
|
||||||
if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) {
|
if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) {
|
||||||
for (final KeywordInterface inst : source.getKeywords()) {
|
for (final KeywordInterface inst : source.getKeywords()) {
|
||||||
@@ -277,7 +288,6 @@ public final class GameActionUtil {
|
|||||||
newSA.setDescription(sb.toString());
|
newSA.setDescription(sb.toString());
|
||||||
|
|
||||||
alternatives.add(newSA);
|
alternatives.add(newSA);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,36 +483,63 @@ public final class GameActionUtil {
|
|||||||
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
|
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
|
||||||
|
|
||||||
if (v > 0) {
|
if (v > 0) {
|
||||||
|
Card eff = createETBCountersEffect(c, host, activator, "P1P1", String.valueOf(v));
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
result = sa.copy();
|
||||||
|
}
|
||||||
|
result.addRollbackEffect(eff);
|
||||||
|
for (int i = 0; i < v; i++) {
|
||||||
|
result.getPayCosts().add(cost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset active Trigger
|
||||||
|
if (reset) {
|
||||||
|
host.getGame().getTriggerHandler().resetActiveTriggers(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result != null ? result : sa;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Card createETBCountersEffect(Card sourceCard, Card c, Player controller, String counter, String amount) {
|
||||||
|
final Game game = sourceCard.getGame();
|
||||||
final Card eff = new Card(game.nextCardId(), game);
|
final Card eff = new Card(game.nextCardId(), game);
|
||||||
eff.setTimestamp(game.getNextTimestamp());
|
eff.setTimestamp(game.getNextTimestamp());
|
||||||
eff.setName(c.getName() + "'s Effect");
|
eff.setName(sourceCard.getName() + "'s Effect");
|
||||||
eff.addType("Effect");
|
eff.addType("Effect");
|
||||||
eff.setOwner(activator);
|
eff.setOwner(controller);
|
||||||
|
|
||||||
eff.setImageKey(c.getImageKey());
|
eff.setImageKey(sourceCard.getImageKey());
|
||||||
eff.setColor(MagicColor.COLORLESS);
|
eff.setColor(MagicColor.COLORLESS);
|
||||||
eff.setImmutable(true);
|
eff.setImmutable(true);
|
||||||
// try to get the SpellAbility from the mana ability
|
// try to get the SpellAbility from the mana ability
|
||||||
//eff.setEffectSource((SpellAbility)null);
|
//eff.setEffectSource((SpellAbility)null);
|
||||||
|
|
||||||
eff.addRemembered(host);
|
eff.addRemembered(c);
|
||||||
|
|
||||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ P1P1 | ETB$ True | CounterNum$ " + v;
|
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + counter
|
||||||
|
+ " | ETB$ True | CounterNum$ " + amount;
|
||||||
|
|
||||||
SpellAbility saAb = AbilityFactory.getAbility(abStr, c);
|
SpellAbility sa = AbilityFactory.getAbility(abStr, c);
|
||||||
|
if (!StringUtils.isNumeric(amount)) {
|
||||||
CardFactoryUtil.setupETBReplacementAbility(saAb);
|
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(v, CounterEnumType.P1P1.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 | Description$ " + desc;
|
||||||
|
|
||||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
||||||
re.setLayer(ReplacementLayer.Other);
|
re.setLayer(ReplacementLayer.Other);
|
||||||
re.setOverridingAbility(saAb);
|
re.setOverridingAbility(sa);
|
||||||
|
|
||||||
eff.addReplacementEffect(re);
|
eff.addReplacementEffect(re);
|
||||||
|
|
||||||
@@ -526,65 +563,28 @@ public final class GameActionUtil {
|
|||||||
game.getAction().moveTo(ZoneType.Command, eff, null);
|
game.getAction().moveTo(ZoneType.Command, eff, null);
|
||||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||||
|
|
||||||
if (result == null) {
|
return eff;
|
||||||
result = sa.copy();
|
|
||||||
}
|
|
||||||
for (int i = 0; i < v; i++) {
|
|
||||||
result.getPayCosts().add(cost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset active Trigger
|
public static String generatedTotalMana(final SpellAbility sa) {
|
||||||
if (reset) {
|
StringBuilder sb = new StringBuilder();
|
||||||
host.getGame().getTriggerHandler().resetActiveTriggers(false);
|
SpellAbility tail = sa;
|
||||||
|
while (tail != null) {
|
||||||
|
String value = generatedMana(tail);
|
||||||
|
if (!value.isEmpty() && !"0".equals(value)) {
|
||||||
|
sb.append(value).append(" ");
|
||||||
}
|
}
|
||||||
|
tail = tail.getSubAbility();
|
||||||
return result != null ? result : sa;
|
|
||||||
}
|
}
|
||||||
|
return sb.toString().trim();
|
||||||
private static boolean hasUrzaLands(final Player p) {
|
|
||||||
final CardCollectionView landsControlled = p.getCardsIn(ZoneType.Battlefield);
|
|
||||||
return Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Mine")))
|
|
||||||
&& Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Power-Plant")))
|
|
||||||
&& Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Tower")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int amountOfManaGenerated(final SpellAbility sa, boolean multiply) {
|
|
||||||
// Calculate generated mana here for stack description and resolving
|
|
||||||
|
|
||||||
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa) : 1;
|
|
||||||
AbilityManaPart abMana = sa.getManaPartRecursive();
|
|
||||||
|
|
||||||
if (sa.hasParam("Bonus")) {
|
|
||||||
// For mana abilities that get a bonus
|
|
||||||
// Bonus currently MULTIPLIES the base amount. Base Amounts should
|
|
||||||
// ALWAYS be Base
|
|
||||||
int bonus = 0;
|
|
||||||
if (sa.getParam("Bonus").equals("UrzaLands")) {
|
|
||||||
if (hasUrzaLands(sa.getActivatingPlayer())) {
|
|
||||||
bonus = Integer.parseInt(sa.getParam("BonusProduced"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
amount += bonus;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!multiply || abMana.isAnyMana() || abMana.isComboMana() || abMana.isSpecialMana()) {
|
|
||||||
return amount;
|
|
||||||
} else {
|
|
||||||
// For cards that produce like {C}{R} vs cards that produce {R}{R}.
|
|
||||||
return abMana.mana().split(" ").length * amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static String generatedMana(final SpellAbility sa) {
|
public static String generatedMana(final SpellAbility sa) {
|
||||||
int amount = amountOfManaGenerated(sa, false);
|
int amount = sa.amountOfManaGenerated(false);
|
||||||
AbilityManaPart abMana = sa.getManaPart();
|
AbilityManaPart abMana = sa.getManaPart();
|
||||||
|
if (abMana == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
String baseMana;
|
String baseMana;
|
||||||
|
|
||||||
if (abMana.isComboMana()) {
|
if (abMana.isComboMana()) {
|
||||||
|
|||||||
@@ -77,4 +77,5 @@ public abstract class TriggerReplacementBase extends CardTraitBase implements II
|
|||||||
this.overridingAbility = overridingAbility0;
|
this.overridingAbility = overridingAbility0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract public SpellAbility ensureAbility();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package forge.game.ability;
|
package forge.game.ability;
|
||||||
|
|
||||||
import forge.game.ability.effects.ChangeZoneAllEffect;
|
|
||||||
import forge.game.ability.effects.ChangeZoneEffect;
|
|
||||||
import forge.game.ability.effects.ManaEffect;
|
|
||||||
import forge.game.ability.effects.ManaReflectedEffect;
|
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.spellability.AbilityActivated;
|
import forge.game.spellability.AbilityActivated;
|
||||||
@@ -15,8 +11,6 @@ import java.util.Map;
|
|||||||
public class AbilityApiBased extends AbilityActivated {
|
public class AbilityApiBased extends AbilityActivated {
|
||||||
private final SpellAbilityEffect effect;
|
private final SpellAbilityEffect effect;
|
||||||
|
|
||||||
private static final long serialVersionUID = -4183793555528531978L;
|
|
||||||
|
|
||||||
public AbilityApiBased(ApiType api0, Card sourceCard, Cost abCost, TargetRestrictions tgt, Map<String, String> params0) {
|
public AbilityApiBased(ApiType api0, Card sourceCard, Cost abCost, TargetRestrictions tgt, Map<String, String> params0) {
|
||||||
super(sourceCard, abCost, tgt);
|
super(sourceCard, abCost, tgt);
|
||||||
originalMapParams.putAll(params0);
|
originalMapParams.putAll(params0);
|
||||||
@@ -24,13 +18,12 @@ public class AbilityApiBased extends AbilityActivated {
|
|||||||
api = api0;
|
api = api0;
|
||||||
effect = api.getSpellEffect();
|
effect = api.getSpellEffect();
|
||||||
|
|
||||||
if (effect instanceof ManaEffect || effect instanceof ManaReflectedEffect) {
|
if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) {
|
||||||
|
|
||||||
this.setManaPart(new AbilityManaPart(sourceCard, mapParams));
|
this.setManaPart(new AbilityManaPart(sourceCard, mapParams));
|
||||||
this.setUndoable(true); // will try at least
|
this.setUndoable(true); // will try at least
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effect instanceof ChangeZoneEffect || effect instanceof ChangeZoneAllEffect) {
|
if (api.equals(ApiType.ChangeZone) || api.equals(ApiType.ChangeZoneAll)) {
|
||||||
AbilityFactory.adjustChangeZoneTarget(mapParams, this);
|
AbilityFactory.adjustChangeZoneTarget(mapParams, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ public final class AbilityFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (api == ApiType.DelayedTrigger && mapParams.containsKey("Execute")) {
|
if ((api == ApiType.DelayedTrigger || api == ApiType.ImmediateTrigger) && mapParams.containsKey("Execute")) {
|
||||||
spellAbility.setSVar(mapParams.get("Execute"), sVarHolder.getSVar(mapParams.get("Execute")));
|
spellAbility.setSVar(mapParams.get("Execute"), sVarHolder.getSVar(mapParams.get("Execute")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,10 +364,6 @@ public final class AbilityFactory {
|
|||||||
if (mapParams.containsKey("TargetsWithDifferentCMC")) {
|
if (mapParams.containsKey("TargetsWithDifferentCMC")) {
|
||||||
abTgt.setDifferentCMC(true);
|
abTgt.setDifferentCMC(true);
|
||||||
}
|
}
|
||||||
if (mapParams.containsKey("DividedAsYouChoose")) {
|
|
||||||
abTgt.calculateStillToDivide(mapParams.get("DividedAsYouChoose"), null, null);
|
|
||||||
abTgt.setDividedAsYouChoose(true);
|
|
||||||
}
|
|
||||||
if (mapParams.containsKey("TargetsAtRandom")) {
|
if (mapParams.containsKey("TargetsAtRandom")) {
|
||||||
abTgt.setRandomTarget(true);
|
abTgt.setRandomTarget(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1400,7 +1400,7 @@ public class AbilityUtils {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// check conditions
|
// check conditions
|
||||||
if (sa.getConditions().areMet(sa)) {
|
if (sa.metConditions()) {
|
||||||
if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) {
|
if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) {
|
||||||
sa.resolve();
|
sa.resolve();
|
||||||
}
|
}
|
||||||
@@ -1668,10 +1668,15 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
// Count$Kicked.<numHB>.<numNotHB>
|
// Count$Kicked.<numHB>.<numNotHB>
|
||||||
if (sq[0].startsWith("Kicked")) {
|
if (sq[0].startsWith("Kicked")) {
|
||||||
boolean kicked = ((SpellAbility)ctb).isKicked() || c.getKickerMagnitude() > 0;
|
boolean kicked = sa.isKicked() || c.getKickerMagnitude() > 0;
|
||||||
return CardFactoryUtil.doXMath(Integer.parseInt(kicked ? sq[1] : sq[2]), expr, c);
|
return CardFactoryUtil.doXMath(Integer.parseInt(kicked ? sq[1] : sq[2]), expr, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Count$UrzaLands.<numHB>.<numNotHB>
|
||||||
|
if (sq[0].startsWith("UrzaLands")) {
|
||||||
|
return CardFactoryUtil.doXMath(Integer.parseInt(sa.getActivatingPlayer().hasUrzaLands() ? sq[1] : sq[2]), expr, c);
|
||||||
|
}
|
||||||
|
|
||||||
//Count$SearchedLibrary.<DefinedPlayer>
|
//Count$SearchedLibrary.<DefinedPlayer>
|
||||||
if (sq[0].contains("SearchedLibrary")) {
|
if (sq[0].contains("SearchedLibrary")) {
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ public enum ApiType {
|
|||||||
Repeat (RepeatEffect.class),
|
Repeat (RepeatEffect.class),
|
||||||
RepeatEach (RepeatEachEffect.class),
|
RepeatEach (RepeatEachEffect.class),
|
||||||
ReplaceEffect (ReplaceEffect.class),
|
ReplaceEffect (ReplaceEffect.class),
|
||||||
|
ReplaceMana (ReplaceManaEffect.class),
|
||||||
ReplaceDamage (ReplaceDamageEffect.class),
|
ReplaceDamage (ReplaceDamageEffect.class),
|
||||||
ReplaceSplitDamage (ReplaceSplitDamageEffect.class),
|
ReplaceSplitDamage (ReplaceSplitDamageEffect.class),
|
||||||
RestartGame (RestartGameEffect.class),
|
RestartGame (RestartGameEffect.class),
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ public abstract class SpellAbilityEffect {
|
|||||||
+ " exile it instead of putting it anywhere else.";
|
+ " exile it instead of putting it anywhere else.";
|
||||||
String effect = "DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ " + zone;
|
String effect = "DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ " + zone;
|
||||||
|
|
||||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true, null);
|
||||||
re.setLayer(ReplacementLayer.Other);
|
re.setLayer(ReplacementLayer.Other);
|
||||||
|
|
||||||
re.setOverridingAbility(AbilityFactory.getAbility(effect, eff));
|
re.setOverridingAbility(AbilityFactory.getAbility(effect, eff));
|
||||||
|
|||||||
@@ -2,10 +2,6 @@ package forge.game.ability;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import forge.game.ability.effects.ChangeZoneAllEffect;
|
|
||||||
import forge.game.ability.effects.ChangeZoneEffect;
|
|
||||||
import forge.game.ability.effects.ManaEffect;
|
|
||||||
import forge.game.ability.effects.ManaReflectedEffect;
|
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.spellability.AbilityManaPart;
|
import forge.game.spellability.AbilityManaPart;
|
||||||
@@ -28,11 +24,11 @@ public class SpellApiBased extends Spell {
|
|||||||
// A spell is always intrinsic
|
// A spell is always intrinsic
|
||||||
this.setIntrinsic(true);
|
this.setIntrinsic(true);
|
||||||
|
|
||||||
if (effect instanceof ManaEffect || effect instanceof ManaReflectedEffect) {
|
if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) {
|
||||||
this.setManaPart(new AbilityManaPart(sourceCard, mapParams));
|
this.setManaPart(new AbilityManaPart(sourceCard, mapParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effect instanceof ChangeZoneEffect || effect instanceof ChangeZoneAllEffect) {
|
if (api.equals(ApiType.ChangeZone) || api.equals(ApiType.ChangeZoneAll)) {
|
||||||
AbilityFactory.adjustChangeZoneTarget(mapParams, this);
|
AbilityFactory.adjustChangeZoneTarget(mapParams, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,8 +159,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
// Grant triggers
|
// Grant triggers
|
||||||
final List<Trigger> addedTriggers = Lists.newArrayList();
|
final List<Trigger> addedTriggers = Lists.newArrayList();
|
||||||
for (final String s : triggers) {
|
for (final String s : triggers) {
|
||||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(AbilityUtils.getSVar(sa, s), c, false);
|
final Trigger parsedTrigger = TriggerHandler.parseTrigger(AbilityUtils.getSVar(sa, s), c, false, sa);
|
||||||
parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(c, parsedTrigger.getParam("Execute"), sa));
|
|
||||||
parsedTrigger.setOriginalHost(source);
|
parsedTrigger.setOriginalHost(source);
|
||||||
addedTriggers.add(parsedTrigger);
|
addedTriggers.add(parsedTrigger);
|
||||||
}
|
}
|
||||||
@@ -168,7 +167,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect {
|
|||||||
// give replacement effects
|
// give replacement effects
|
||||||
final List<ReplacementEffect> addedReplacements = Lists.newArrayList();
|
final List<ReplacementEffect> addedReplacements = Lists.newArrayList();
|
||||||
for (final String s : replacements) {
|
for (final String s : replacements) {
|
||||||
addedReplacements.add(ReplacementHandler.parseReplacement(AbilityUtils.getSVar(sa, s), c, false));
|
addedReplacements.add(ReplacementHandler.parseReplacement(AbilityUtils.getSVar(sa, s), c, false, sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
// give static abilities (should only be used by cards to give
|
// give static abilities (should only be used by cards to give
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
|
import forge.game.GameObjectPredicates;
|
||||||
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;
|
||||||
@@ -42,9 +44,6 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean changesOneTarget = sa.hasParam("ChangeSingleTarget"); // The only card known to replace targets with self is Spellskite
|
|
||||||
// There is also Muck Drubb but it replaces ALL occurences of a single target with itself (unlike Spellskite that has to be activated for each).
|
|
||||||
|
|
||||||
SpellAbilityStackInstance changingTgtSI = si;
|
SpellAbilityStackInstance changingTgtSI = si;
|
||||||
Player chooser = sa.getActivatingPlayer();
|
Player chooser = sa.getActivatingPlayer();
|
||||||
|
|
||||||
@@ -54,7 +53,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
|||||||
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()))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (changesOneTarget) {
|
if (sa.hasParam("ChangeSingleTarget")) {
|
||||||
// 1. choose a target of target spell
|
// 1. choose a target of target spell
|
||||||
List<Pair<SpellAbilityStackInstance, GameObject>> allTargets = new ArrayList<>();
|
List<Pair<SpellAbilityStackInstance, GameObject>> allTargets = new ArrayList<>();
|
||||||
while(changingTgtSI != null) {
|
while(changingTgtSI != null) {
|
||||||
@@ -77,6 +76,8 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
|||||||
GameObject oldTarget = chosenTarget.getValue();
|
GameObject oldTarget = chosenTarget.getValue();
|
||||||
TargetChoices oldTargetBlock = replaceIn.getTargetChoices();
|
TargetChoices oldTargetBlock = replaceIn.getTargetChoices();
|
||||||
TargetChoices newTargetBlock = oldTargetBlock.clone();
|
TargetChoices newTargetBlock = oldTargetBlock.clone();
|
||||||
|
// gets the divied value from old target
|
||||||
|
Integer div = oldTargetBlock.getDividedValue(oldTarget);
|
||||||
newTargetBlock.remove(oldTarget);
|
newTargetBlock.remove(oldTarget);
|
||||||
replaceIn.updateTarget(newTargetBlock);
|
replaceIn.updateTarget(newTargetBlock);
|
||||||
// 3. test if updated choices would be correct.
|
// 3. test if updated choices would be correct.
|
||||||
@@ -84,7 +85,10 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (replaceIn.getSpellAbility(true).canTarget(newTarget)) {
|
if (replaceIn.getSpellAbility(true).canTarget(newTarget)) {
|
||||||
newTargetBlock.add(newTarget);
|
newTargetBlock.add(newTarget);
|
||||||
replaceIn.updateTarget(newTargetBlock, oldTarget, newTarget);
|
if (div != null) {
|
||||||
|
newTargetBlock.addDividedAllocation(newTarget, div);
|
||||||
|
}
|
||||||
|
replaceIn.updateTarget(newTargetBlock);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
replaceIn.updateTarget(oldTargetBlock);
|
replaceIn.updateTarget(oldTargetBlock);
|
||||||
@@ -93,36 +97,38 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
|||||||
else {
|
else {
|
||||||
while(changingTgtSI != null) {
|
while(changingTgtSI != null) {
|
||||||
SpellAbility changingTgtSA = changingTgtSI.getSpellAbility(true);
|
SpellAbility changingTgtSA = changingTgtSI.getSpellAbility(true);
|
||||||
if (sa.hasParam("RandomTarget")){
|
|
||||||
if (changingTgtSA.usesTargeting()) {
|
if (changingTgtSA.usesTargeting()) {
|
||||||
|
// random target and DefinedMagnet works on single targets
|
||||||
|
if (sa.hasParam("RandomTarget")){
|
||||||
|
int div = changingTgtSA.getTotalDividedValue();
|
||||||
changingTgtSA.resetTargets();
|
changingTgtSA.resetTargets();
|
||||||
List<GameEntity> candidates = changingTgtSA.getTargetRestrictions().getAllCandidates(changingTgtSA, true);
|
List<GameEntity> candidates = changingTgtSA.getTargetRestrictions().getAllCandidates(changingTgtSA, true);
|
||||||
GameEntity choice = Aggregates.random(candidates);
|
GameEntity choice = Aggregates.random(candidates);
|
||||||
changingTgtSA.getTargets().add(choice);
|
changingTgtSA.getTargets().add(choice);
|
||||||
changingTgtSI.updateTarget(changingTgtSA.getTargets(), null, choice);
|
if (changingTgtSA.isDividedAsYouChoose()) {
|
||||||
|
changingTgtSA.addDividedAllocation(choice, div);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changingTgtSI.updateTarget(changingTgtSA.getTargets());
|
||||||
}
|
}
|
||||||
else if (sa.hasParam("DefinedMagnet")){
|
else if (sa.hasParam("DefinedMagnet")){
|
||||||
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
|
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
|
||||||
if (newTarget != null && changingTgtSA.canTarget(newTarget)) {
|
if (newTarget != null && changingTgtSA.canTarget(newTarget)) {
|
||||||
|
int div = changingTgtSA.getTotalDividedValue();
|
||||||
changingTgtSA.resetTargets();
|
changingTgtSA.resetTargets();
|
||||||
changingTgtSA.getTargets().add(newTarget);
|
changingTgtSA.getTargets().add(newTarget);
|
||||||
changingTgtSI.updateTarget(changingTgtSA.getTargets(), null, newTarget);
|
changingTgtSI.updateTarget(changingTgtSA.getTargets());
|
||||||
|
if (changingTgtSA.isDividedAsYouChoose()) {
|
||||||
|
changingTgtSA.addDividedAllocation(newTarget, div);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Update targets, with a potential new target
|
// Update targets, with a potential new target
|
||||||
TargetChoices newTarget = sa.getActivatingPlayer().getController().chooseNewTargetsFor(changingTgtSA);
|
Predicate<GameObject> filter = sa.hasParam("TargetRestriction") ? GameObjectPredicates.restriction(sa.getParam("TargetRestriction").split(","), activator, sa.getHostCard(), sa) : null;
|
||||||
|
// TODO Creature.Other might not work yet as it should
|
||||||
|
TargetChoices newTarget = sa.getActivatingPlayer().getController().chooseNewTargetsFor(changingTgtSA, filter, false);
|
||||||
if (null != newTarget) {
|
if (null != newTarget) {
|
||||||
if (sa.hasParam("TargetRestriction")) {
|
|
||||||
if (newTarget.getFirstTargetedCard() != null && newTarget.getFirstTargetedCard().
|
|
||||||
isValid(sa.getParam("TargetRestriction").split(","), activator, sa.getHostCard(), sa)) {
|
|
||||||
changingTgtSI.updateTarget(newTarget);
|
|
||||||
} else if (newTarget.getFirstTargetedPlayer() != null && newTarget.getFirstTargetedPlayer().
|
|
||||||
isValid(sa.getParam("TargetRestriction").split(","), activator, sa.getHostCard(), sa)) {
|
|
||||||
changingTgtSI.updateTarget(newTarget);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
changingTgtSI.updateTarget(newTarget);
|
changingTgtSI.updateTarget(newTarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ public class CloneEffect extends SpellAbilityEffect {
|
|||||||
if (!cloneTargets.isEmpty()) {
|
if (!cloneTargets.isEmpty()) {
|
||||||
tgtCard = cloneTargets.get(0);
|
tgtCard = cloneTargets.get(0);
|
||||||
game.getTriggerHandler().clearActiveTriggers(tgtCard, null);
|
game.getTriggerHandler().clearActiveTriggers(tgtCard, null);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else if (sa.hasParam("Choices") && sa.usesTargeting()) {
|
} else if (sa.hasParam("Choices") && sa.usesTargeting()) {
|
||||||
tgtCard = sa.getTargets().getFirstTargetedCard();
|
tgtCard = sa.getTargets().getFirstTargetedCard();
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
candidates.remove(p);
|
candidates.remove(p);
|
||||||
|
|
||||||
for (GameEntity o : candidates) {
|
for (GameEntity o : candidates) {
|
||||||
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
|
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
|
||||||
resetFirstTargetOnCopy(copy, o, targetedSA);
|
resetFirstTargetOnCopy(copy, o, targetedSA);
|
||||||
copies.add(copy);
|
copies.add(copy);
|
||||||
}
|
}
|
||||||
@@ -144,12 +144,12 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (final Card c : valid) {
|
for (final Card c : valid) {
|
||||||
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
|
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
|
||||||
resetFirstTargetOnCopy(copy, c, targetedSA);
|
resetFirstTargetOnCopy(copy, c, targetedSA);
|
||||||
copies.add(copy);
|
copies.add(copy);
|
||||||
}
|
}
|
||||||
for (final Player p : players) {
|
for (final Player p : players) {
|
||||||
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
|
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
|
||||||
resetFirstTargetOnCopy(copy, p, targetedSA);
|
resetFirstTargetOnCopy(copy, p, targetedSA);
|
||||||
copies.add(copy);
|
copies.add(copy);
|
||||||
}
|
}
|
||||||
@@ -157,12 +157,9 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
|
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
|
||||||
if (sa.hasParam("MayChooseTarget")) {
|
if (sa.hasParam("MayChooseTarget")) {
|
||||||
copy.setMayChooseNewTargets(true);
|
copy.setMayChooseNewTargets(true);
|
||||||
if (copy.usesTargeting()) {
|
|
||||||
copy.getTargetRestrictions().setMandatory(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// extra case for Epic to remove the keyword and the last part of the SpellAbility
|
// extra case for Epic to remove the keyword and the last part of the SpellAbility
|
||||||
@@ -206,12 +203,9 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
int extraAmount = addAmount - copies.size();
|
int extraAmount = addAmount - copies.size();
|
||||||
for (int i = 0; i < extraAmount; i++) {
|
for (int i = 0; i < extraAmount; i++) {
|
||||||
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
|
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
|
||||||
// extra copies added with CopySpellReplacenment currently always has new choose targets
|
// extra copies added with CopySpellReplacenment currently always has new choose targets
|
||||||
copy.setMayChooseNewTargets(true);
|
copy.setMayChooseNewTargets(true);
|
||||||
if (copy.usesTargeting()) {
|
|
||||||
copy.getTargetRestrictions().setMandatory(true);
|
|
||||||
}
|
|
||||||
copies.add(copy);
|
copies.add(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,13 +27,12 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
final Card host = sa.getHostCard();
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
final List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
final List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
||||||
|
|
||||||
Card source = null;
|
Card source = null;
|
||||||
if (sa.usesTargeting() && sa.getTargetRestrictions().getMinTargets(host, sa) == 2) {
|
if (sa.usesTargeting() && sa.getMinTargets() == 2) {
|
||||||
if (tgtCards.size() < 2) {
|
if (tgtCards.size() < 2) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -241,7 +240,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
|
|||||||
Card source = null;
|
Card source = null;
|
||||||
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
|
||||||
// special logic for moving from Target to Target
|
// special logic for moving from Target to Target
|
||||||
if (sa.usesTargeting() && sa.getTargetRestrictions().getMinTargets(host, sa) == 2) {
|
if (sa.usesTargeting() && sa.getMinTargets() == 2) {
|
||||||
if (tgtCards.size() < 2) {
|
if (tgtCards.size() < 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
protected String getStackDescription(SpellAbility spellAbility) {
|
protected String getStackDescription(SpellAbility spellAbility) {
|
||||||
final StringBuilder stringBuilder = new StringBuilder();
|
final StringBuilder stringBuilder = new StringBuilder();
|
||||||
final Card card = spellAbility.getHostCard();
|
final Card card = spellAbility.getHostCard();
|
||||||
final boolean dividedAsYouChoose = spellAbility.hasParam("DividedAsYouChoose");
|
|
||||||
|
|
||||||
final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"), spellAbility);
|
final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"), spellAbility);
|
||||||
//skip the StringBuilder if no targets are chosen ("up to" scenario)
|
//skip the StringBuilder if no targets are chosen ("up to" scenario)
|
||||||
@@ -60,7 +59,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
stringBuilder.append("Bolster ").append(amount);
|
stringBuilder.append("Bolster ").append(amount);
|
||||||
return stringBuilder.toString();
|
return stringBuilder.toString();
|
||||||
}
|
}
|
||||||
if (dividedAsYouChoose) {
|
if (spellAbility.isDividedAsYouChoose()) {
|
||||||
stringBuilder.append("Distribute ");
|
stringBuilder.append("Distribute ");
|
||||||
} else {
|
} else {
|
||||||
stringBuilder.append("Put ");
|
stringBuilder.append("Put ");
|
||||||
@@ -84,7 +83,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
stringBuilder.append("s");
|
stringBuilder.append("s");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dividedAsYouChoose) {
|
if (spellAbility.isDividedAsYouChoose()) {
|
||||||
stringBuilder.append(" among ");
|
stringBuilder.append(" among ");
|
||||||
} else {
|
} else {
|
||||||
stringBuilder.append(" on ");
|
stringBuilder.append(" on ");
|
||||||
@@ -96,8 +95,9 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
for(int i = 0; i < targetCards.size(); i++) {
|
for(int i = 0; i < targetCards.size(); i++) {
|
||||||
Card targetCard = targetCards.get(i);
|
Card targetCard = targetCards.get(i);
|
||||||
stringBuilder.append(targetCard);
|
stringBuilder.append(targetCard);
|
||||||
if (spellAbility.getTargetRestrictions().getDividedMap().get(targetCard) != null) // fix null counter stack description
|
Integer v = spellAbility.getDividedValue(targetCard);
|
||||||
stringBuilder.append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetCard)).append(" counter)");
|
if (v != null) // fix null counter stack description
|
||||||
|
stringBuilder.append(" (").append(v).append(" counter)");
|
||||||
|
|
||||||
if(i == targetCards.size() - 2) {
|
if(i == targetCards.size() - 2) {
|
||||||
stringBuilder.append(" and ");
|
stringBuilder.append(" and ");
|
||||||
@@ -259,7 +259,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (obj instanceof Card) {
|
if (obj instanceof Card) {
|
||||||
boolean counterAdded = false;
|
boolean counterAdded = false;
|
||||||
counterAmount = sa.usesTargeting() && sa.hasParam("DividedAsYouChoose") ? sa.getTargetRestrictions().getDividedValue(gameCard) : counterAmount;
|
counterAmount = sa.usesTargeting() && sa.isDividedAsYouChoose() ? sa.getDividedValue(gameCard) : counterAmount;
|
||||||
if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) {
|
if (!sa.usesTargeting() || gameCard.canBeTargetedBy(sa)) {
|
||||||
if (max != -1) {
|
if (max != -1) {
|
||||||
counterAmount = Math.max(Math.min(max - gameCard.getCounters(counterType), counterAmount), 0);
|
counterAmount = Math.max(Math.min(max - gameCard.getCounters(counterType), counterAmount), 0);
|
||||||
@@ -270,7 +270,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
params.put("CounterType", counterType);
|
params.put("CounterType", counterType);
|
||||||
counterAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyCounters"), 0, counterAmount, params);
|
counterAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyCounters"), 0, counterAmount, params);
|
||||||
}
|
}
|
||||||
if (sa.hasParam("DividedAsYouChoose") && !sa.usesTargeting()) {
|
if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) {
|
||||||
Map<String, Object> params = Maps.newHashMap();
|
Map<String, Object> params = Maps.newHashMap();
|
||||||
params.put("Target", obj);
|
params.put("Target", obj);
|
||||||
params.put("CounterType", counterType);
|
params.put("CounterType", counterType);
|
||||||
@@ -378,7 +378,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
card.addRemembered(gameCard);
|
card.addRemembered(gameCard);
|
||||||
}
|
}
|
||||||
game.updateLastStateForCard(gameCard);
|
game.updateLastStateForCard(gameCard);
|
||||||
if (sa.hasParam("DividedAsYouChoose") && !sa.usesTargeting()) {
|
if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) {
|
||||||
counterRemain = counterRemain - counterAmount;
|
counterRemain = counterRemain - counterAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
if (spellAbility.usesTargeting()) {
|
if (spellAbility.usesTargeting()) {
|
||||||
if (spellAbility.hasParam("DivideEvenly")) {
|
if (spellAbility.hasParam("DivideEvenly")) {
|
||||||
stringBuilder.append("divided evenly (rounded down) to\n");
|
stringBuilder.append("divided evenly (rounded down) to\n");
|
||||||
} else if (spellAbility.hasParam("DividedAsYouChoose")) {
|
} else if (spellAbility.isDividedAsYouChoose()) {
|
||||||
stringBuilder.append("divided to\n");
|
stringBuilder.append("divided to\n");
|
||||||
} else
|
} else
|
||||||
stringBuilder.append("to ");
|
stringBuilder.append("to ");
|
||||||
@@ -75,8 +75,9 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
for (int i = 0; i < targetCards.size(); i++) {
|
for (int i = 0; i < targetCards.size(); i++) {
|
||||||
Card targetCard = targetCards.get(i);
|
Card targetCard = targetCards.get(i);
|
||||||
stringBuilder.append(targetCard);
|
stringBuilder.append(targetCard);
|
||||||
if (spellAbility.getTargetRestrictions().getDividedMap().get(targetCard) != null) //fix null damage stack description
|
Integer v = spellAbility.getDividedValue(targetCard);
|
||||||
stringBuilder.append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetCard)).append(" damage)");
|
if (v != null) //fix null damage stack description
|
||||||
|
stringBuilder.append(" (").append(v).append(" damage)");
|
||||||
|
|
||||||
if (i == targetCount - 2) {
|
if (i == targetCount - 2) {
|
||||||
stringBuilder.append(" and ");
|
stringBuilder.append(" and ");
|
||||||
@@ -89,8 +90,9 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
for (int i = 0; i < players.size(); i++) {
|
for (int i = 0; i < players.size(); i++) {
|
||||||
Player targetPlayer = players.get(i);
|
Player targetPlayer = players.get(i);
|
||||||
stringBuilder.append(targetPlayer);
|
stringBuilder.append(targetPlayer);
|
||||||
if (spellAbility.getTargetRestrictions().getDividedMap().get(targetPlayer) != null) //fix null damage stack description
|
Integer v = spellAbility.getDividedValue(targetPlayer);
|
||||||
stringBuilder.append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetPlayer)).append(" damage)");
|
if (v != null) //fix null damage stack description
|
||||||
|
stringBuilder.append(" (").append(v).append(" damage)");
|
||||||
|
|
||||||
if (i == players.size() - 2) {
|
if (i == players.size() - 2) {
|
||||||
stringBuilder.append(" and ");
|
stringBuilder.append(" and ");
|
||||||
@@ -102,7 +104,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
} else {
|
} else {
|
||||||
if (spellAbility.hasParam("DivideEvenly")) {
|
if (spellAbility.hasParam("DivideEvenly")) {
|
||||||
stringBuilder.append("divided evenly (rounded down) ");
|
stringBuilder.append("divided evenly (rounded down) ");
|
||||||
} else if (spellAbility.hasParam("DividedAsYouChoose")) {
|
} else if (spellAbility.isDividedAsYouChoose()) {
|
||||||
stringBuilder.append("divided as you choose ");
|
stringBuilder.append("divided as you choose ");
|
||||||
}
|
}
|
||||||
stringBuilder.append("to ").append(Lang.joinHomogenous(targets));
|
stringBuilder.append("to ").append(Lang.joinHomogenous(targets));
|
||||||
@@ -229,7 +231,7 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
|
|
||||||
for (final GameObject o : tgts) {
|
for (final GameObject o : tgts) {
|
||||||
if (!removeDamage) {
|
if (!removeDamage) {
|
||||||
dmg = (sa.usesTargeting() && sa.hasParam("DividedAsYouChoose")) ? sa.getTargetRestrictions().getDividedValue(o) : dmg;
|
dmg = (sa.usesTargeting() && sa.isDividedAsYouChoose()) ? sa.getDividedValue(o) : dmg;
|
||||||
if (dmg <= 0) {
|
if (dmg <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class DamagePreventEffect extends SpellAbilityEffect {
|
|||||||
sb.append("Prevent the next ");
|
sb.append("Prevent the next ");
|
||||||
sb.append(sa.getParam("Amount"));
|
sb.append(sa.getParam("Amount"));
|
||||||
sb.append(" damage that would be dealt ");
|
sb.append(" damage that would be dealt ");
|
||||||
if (sa.hasParam("DividedAsYouChoose")) {
|
if (sa.isDividedAsYouChoose()) {
|
||||||
sb.append("between ");
|
sb.append("between ");
|
||||||
} else {
|
} else {
|
||||||
sb.append("to ");
|
sb.append("to ");
|
||||||
@@ -75,8 +75,8 @@ public class DamagePreventEffect extends SpellAbilityEffect {
|
|||||||
final boolean targeted = (sa.usesTargeting());
|
final boolean targeted = (sa.usesTargeting());
|
||||||
final boolean preventionWithEffect = sa.hasParam("PreventionSubAbility");
|
final boolean preventionWithEffect = sa.hasParam("PreventionSubAbility");
|
||||||
|
|
||||||
for (final Object o : tgts) {
|
for (final GameObject o : tgts) {
|
||||||
numDam = (sa.usesTargeting() && sa.hasParam("DividedAsYouChoose")) ? sa.getTargetRestrictions().getDividedValue(o) : numDam;
|
numDam = (sa.usesTargeting() && sa.isDividedAsYouChoose()) ? sa.getDividedValue(o) : numDam;
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card) {
|
||||||
final Card c = (Card) o;
|
final Card c = (Card) o;
|
||||||
if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) {
|
if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class DelayedTriggerEffect extends SpellAbilityEffect {
|
|||||||
Card lki = CardUtil.getLKICopy(gameCard);
|
Card lki = CardUtil.getLKICopy(gameCard);
|
||||||
lki.clearControllers();
|
lki.clearControllers();
|
||||||
lki.setOwner(sa.getActivatingPlayer());
|
lki.setOwner(sa.getActivatingPlayer());
|
||||||
final Trigger delTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic());
|
final Trigger delTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic(), null);
|
||||||
delTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true));
|
delTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true));
|
||||||
|
|
||||||
if (triggerRemembered != null) {
|
if (triggerRemembered != null) {
|
||||||
@@ -81,7 +81,7 @@ public class DelayedTriggerEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mapParams.containsKey("Execute") || sa.hasAdditionalAbility("Execute")) {
|
if (sa.hasAdditionalAbility("Execute")) {
|
||||||
AbilitySub overridingSA = (AbilitySub)sa.getAdditionalAbility("Execute").copy(lki, sa.getActivatingPlayer(), false);
|
AbilitySub overridingSA = (AbilitySub)sa.getAdditionalAbility("Execute").copy(lki, sa.getActivatingPlayer(), false);
|
||||||
// need to reset the parent, additionalAbility does set it to this
|
// need to reset the parent, additionalAbility does set it to this
|
||||||
overridingSA.setParent(null);
|
overridingSA.setParent(null);
|
||||||
@@ -96,7 +96,7 @@ public class DelayedTriggerEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
delTrig.setOverridingAbility(overridingSA);
|
delTrig.setOverridingAbility(overridingSA);
|
||||||
}
|
}
|
||||||
final TriggerHandler trigHandler = sa.getActivatingPlayer().getGame().getTriggerHandler();
|
final TriggerHandler trigHandler = game.getTriggerHandler();
|
||||||
if (mapParams.containsKey("DelayedTriggerDefinedPlayer")) { // on sb's next turn
|
if (mapParams.containsKey("DelayedTriggerDefinedPlayer")) { // on sb's next turn
|
||||||
Player p = Iterables.getFirst(AbilityUtils.getDefinedPlayers(sa.getHostCard(), mapParams.get("DelayedTriggerDefinedPlayer"), sa), null);
|
Player p = Iterables.getFirst(AbilityUtils.getDefinedPlayers(sa.getHostCard(), mapParams.get("DelayedTriggerDefinedPlayer"), sa), null);
|
||||||
trigHandler.registerPlayerDefinedDelayedTrigger(p, delTrig);
|
trigHandler.registerPlayerDefinedDelayedTrigger(p, delTrig);
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ public class EffectEffect extends SpellAbilityEffect {
|
|||||||
for (final String s : effectReplacementEffects) {
|
for (final String s : effectReplacementEffects) {
|
||||||
final String actualReplacement = AbilityUtils.getSVar(sa, s);
|
final String actualReplacement = AbilityUtils.getSVar(sa, s);
|
||||||
|
|
||||||
final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, eff, true);
|
final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, eff, true, sa);
|
||||||
parsedReplacement.setActiveZone(EnumSet.of(ZoneType.Command));
|
parsedReplacement.setActiveZone(EnumSet.of(ZoneType.Command));
|
||||||
parsedReplacement.setIntrinsic(true);
|
parsedReplacement.setIntrinsic(true);
|
||||||
eff.addReplacementEffect(parsedReplacement);
|
eff.addReplacementEffect(parsedReplacement);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
|
|||||||
Card lki = CardUtil.getLKICopy(gameCard);
|
Card lki = CardUtil.getLKICopy(gameCard);
|
||||||
lki.clearControllers();
|
lki.clearControllers();
|
||||||
lki.setOwner(sa.getActivatingPlayer());
|
lki.setOwner(sa.getActivatingPlayer());
|
||||||
final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic());
|
final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic(), null);
|
||||||
immediateTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true));
|
immediateTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true));
|
||||||
|
|
||||||
// Need to copy paid costs
|
// Need to copy paid costs
|
||||||
@@ -74,7 +74,7 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mapParams.containsKey("Execute") || sa.hasAdditionalAbility("Execute")) {
|
if (sa.hasAdditionalAbility("Execute")) {
|
||||||
AbilitySub overridingSA = (AbilitySub)sa.getAdditionalAbility("Execute").copy(lki, sa.getActivatingPlayer(), false);
|
AbilitySub overridingSA = (AbilitySub)sa.getAdditionalAbility("Execute").copy(lki, sa.getActivatingPlayer(), false);
|
||||||
// need to set Parent to null, otherwise it might have wrong root ability
|
// need to set Parent to null, otherwise it might have wrong root ability
|
||||||
overridingSA.setParent(null);
|
overridingSA.setParent(null);
|
||||||
@@ -85,9 +85,8 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
immediateTrig.setOverridingAbility(overridingSA);
|
immediateTrig.setOverridingAbility(overridingSA);
|
||||||
}
|
}
|
||||||
final TriggerHandler trigHandler = sa.getActivatingPlayer().getGame().getTriggerHandler();
|
|
||||||
|
|
||||||
// Instead of registering this, add to the delayed triggers as an immediate trigger type? Which means it'll fire as soon as possible
|
// Instead of registering this, add to the delayed triggers as an immediate trigger type? Which means it'll fire as soon as possible
|
||||||
trigHandler.registerDelayedTrigger(immediateTrig);
|
game.getTriggerHandler().registerDelayedTrigger(immediateTrig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import forge.game.mana.Mana;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilityManaPart;
|
import forge.game.spellability.AbilityManaPart;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
|
|
||||||
@@ -37,7 +36,6 @@ public class ManaEffect extends SpellAbilityEffect {
|
|||||||
sa.setUndoable(sa.isAbility() && sa.isUndoable());
|
sa.setUndoable(sa.isAbility() && sa.isUndoable());
|
||||||
|
|
||||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
|
||||||
final boolean optional = sa.hasParam("Optional");
|
final boolean optional = sa.hasParam("Optional");
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
final Game game = sa.getActivatingPlayer().getGame();
|
||||||
|
|
||||||
@@ -45,41 +43,21 @@ public class ManaEffect extends SpellAbilityEffect {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("DoubleManaInPool")) {
|
|
||||||
for (final Player player : tgtPlayers) {
|
|
||||||
for (byte color : ManaAtom.MANATYPES) {
|
|
||||||
int amountColor = player.getManaPool().getAmountOfColor(color);
|
|
||||||
for (int i = 0; i < amountColor; i++) {
|
|
||||||
abMana.produceMana(MagicColor.toShortString(color), player, sa);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("ProduceNoOtherMana")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (abMana.isComboMana()) {
|
|
||||||
for (Player p : tgtPlayers) {
|
for (Player p : tgtPlayers) {
|
||||||
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(card, sa.getParam("Amount"), sa) : 1;
|
if (sa.usesTargeting() && !p.canBeTargetedBy(sa)) {
|
||||||
if (tgt != null && !p.canBeTargetedBy(sa)) {
|
|
||||||
// Illegal target. Skip.
|
// Illegal target. Skip.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player activator = sa.getActivatingPlayer();
|
if (abMana.isComboMana()) {
|
||||||
|
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(card, sa.getParam("Amount"), sa) : 1;
|
||||||
|
|
||||||
String express = abMana.getExpressChoice();
|
String express = abMana.getExpressChoice();
|
||||||
String[] colorsProduced = abMana.getComboColors().split(" ");
|
String[] colorsProduced = abMana.getComboColors().split(" ");
|
||||||
|
|
||||||
final StringBuilder choiceString = new StringBuilder();
|
final StringBuilder choiceString = new StringBuilder();
|
||||||
ColorSet colorOptions = null;
|
ColorSet colorOptions = ColorSet.fromNames(colorsProduced);
|
||||||
String[] colorsNeeded = express.isEmpty() ? null : express.split(" ");
|
String[] colorsNeeded = express.isEmpty() ? null : express.split(" ");
|
||||||
if (!abMana.isAnyMana()) {
|
|
||||||
colorOptions = ColorSet.fromNames(colorsProduced);
|
|
||||||
} else {
|
|
||||||
colorOptions = ColorSet.fromNames(MagicColor.Constant.ONLY_COLORS);
|
|
||||||
}
|
|
||||||
boolean differentChoice = abMana.getOrigProduced().contains("Different");
|
boolean differentChoice = abMana.getOrigProduced().contains("Different");
|
||||||
ColorSet fullOptions = colorOptions;
|
ColorSet fullOptions = colorOptions;
|
||||||
for (int nMana = 0; nMana < amount; nMana++) {
|
for (int nMana = 0; nMana < amount; nMana++) {
|
||||||
@@ -93,10 +71,10 @@ public class ManaEffect extends SpellAbilityEffect {
|
|||||||
// just use the first possible color.
|
// just use the first possible color.
|
||||||
choice = colorsProduced[differentChoice ? nMana : 0];
|
choice = colorsProduced[differentChoice ? nMana : 0];
|
||||||
} else {
|
} else {
|
||||||
byte chosenColor = activator.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa,
|
byte chosenColor = p.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa,
|
||||||
differentChoice ? fullOptions : colorOptions);
|
differentChoice ? fullOptions : colorOptions);
|
||||||
if (chosenColor == 0)
|
if (chosenColor == 0)
|
||||||
throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + activator + " color mana choice is empty for " + card.getName());
|
throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + p + " color mana choice is empty for " + card.getName());
|
||||||
|
|
||||||
fullOptions = ColorSet.fromMask(fullOptions.getMyColor() - chosenColor);
|
fullOptions = ColorSet.fromMask(fullOptions.getMyColor() - chosenColor);
|
||||||
choice = MagicColor.toShortString(chosenColor);
|
choice = MagicColor.toShortString(chosenColor);
|
||||||
@@ -116,18 +94,10 @@ public class ManaEffect extends SpellAbilityEffect {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
game.action.nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", activator.getName(), choiceString), activator);
|
game.getAction().nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), choiceString), p);
|
||||||
abMana.setExpressChoice(choiceString.toString());
|
abMana.setExpressChoice(choiceString.toString());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (abMana.isAnyMana()) {
|
else if (abMana.isAnyMana()) {
|
||||||
for (Player p : tgtPlayers) {
|
|
||||||
if (tgt != null && !p.canBeTargetedBy(sa)) {
|
|
||||||
// Illegal target. Skip.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player act = sa.getActivatingPlayer();
|
|
||||||
// AI color choice is set in ComputerUtils so only human players need to make a choice
|
// AI color choice is set in ComputerUtils so only human players need to make a choice
|
||||||
|
|
||||||
String colorsNeeded = abMana.getExpressChoice();
|
String colorsNeeded = abMana.getExpressChoice();
|
||||||
@@ -142,20 +112,14 @@ public class ManaEffect extends SpellAbilityEffect {
|
|||||||
colorMenu = mask == 0 ? ColorSet.ALL_COLORS : ColorSet.fromMask(mask);
|
colorMenu = mask == 0 ? ColorSet.ALL_COLORS : ColorSet.fromMask(mask);
|
||||||
byte val = p.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, colorMenu);
|
byte val = p.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, colorMenu);
|
||||||
if (0 == val) {
|
if (0 == val) {
|
||||||
throw new RuntimeException("ManaEffect::resolve() /*any mana*/ - " + act + " color mana choice is empty for " + card.getName());
|
throw new RuntimeException("ManaEffect::resolve() /*any mana*/ - " + p + " color mana choice is empty for " + card.getName());
|
||||||
}
|
}
|
||||||
choice = MagicColor.toShortString(val);
|
choice = MagicColor.toShortString(val);
|
||||||
|
|
||||||
game.action.nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", act.getName(), choice), act);
|
game.getAction().nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), choice), p);
|
||||||
abMana.setExpressChoice(choice);
|
abMana.setExpressChoice(choice);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (abMana.isSpecialMana()) {
|
else if (abMana.isSpecialMana()) {
|
||||||
for (Player p : tgtPlayers) {
|
|
||||||
if (tgt != null && !p.canBeTargetedBy(sa)) {
|
|
||||||
// Illegal target. Skip.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String type = abMana.getOrigProduced().split("Special ")[1];
|
String type = abMana.getOrigProduced().split("Special ")[1];
|
||||||
|
|
||||||
@@ -177,7 +141,7 @@ public class ManaEffect extends SpellAbilityEffect {
|
|||||||
if (cs.isMonoColor())
|
if (cs.isMonoColor())
|
||||||
sb.append(MagicColor.toShortString(s.getColorMask()));
|
sb.append(MagicColor.toShortString(s.getColorMask()));
|
||||||
else /* (cs.isMulticolor()) */ {
|
else /* (cs.isMulticolor()) */ {
|
||||||
byte chosenColor = sa.getActivatingPlayer().getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
|
byte chosenColor = p.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
|
||||||
sb.append(MagicColor.toShortString(chosenColor));
|
sb.append(MagicColor.toShortString(chosenColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,34 +174,36 @@ public class ManaEffect extends SpellAbilityEffect {
|
|||||||
abMana.setExpressChoice(ColorSet.fromMask(colors));
|
abMana.setExpressChoice(ColorSet.fromMask(colors));
|
||||||
} else if (type.startsWith("EachColoredManaSymbol")) {
|
} else if (type.startsWith("EachColoredManaSymbol")) {
|
||||||
final String res = type.split("_")[1];
|
final String res = type.split("_")[1];
|
||||||
final CardCollection list = AbilityUtils.getDefinedCards(card, res, sa);
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (Card c : list) {
|
for (Card c : AbilityUtils.getDefinedCards(card, res, sa)) {
|
||||||
String mana = c.getManaCost().toString();
|
for (ManaCostShard s : c.getManaCost()) {
|
||||||
for (int i = 0; i < mana.length(); i++) {
|
ColorSet cs = ColorSet.fromMask(s.getColorMask());
|
||||||
char symbol = mana.charAt(i);
|
if(cs.isColorless())
|
||||||
switch (symbol) {
|
continue;
|
||||||
case 'W':
|
sb.append(' ');
|
||||||
case 'U':
|
if (cs.isMonoColor())
|
||||||
case 'B':
|
sb.append(MagicColor.toShortString(s.getColorMask()));
|
||||||
case 'R':
|
else /* (cs.isMulticolor()) */ {
|
||||||
case 'G':
|
byte chosenColor = p.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
|
||||||
sb.append(symbol).append(' ');
|
sb.append(MagicColor.toShortString(chosenColor));
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
abMana.setExpressChoice(sb.toString().trim());
|
abMana.setExpressChoice(sb.toString().trim());
|
||||||
|
} else if (type.startsWith("DoubleManaInPool")) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte color : ManaAtom.MANATYPES) {
|
||||||
|
sb.append(StringUtils.repeat(MagicColor.toShortString(color), " ", p.getManaPool().getAmountOfColor(color))).append(" ");
|
||||||
|
}
|
||||||
|
abMana.setExpressChoice(sb.toString().trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abMana.getExpressChoice().isEmpty()) {
|
if (abMana.getExpressChoice().isEmpty()) {
|
||||||
System.out.println("AbilityFactoryMana::manaResolve() - special mana effect is empty for " + sa.getHostCard().getName());
|
System.out.println("AbilityFactoryMana::manaResolve() - special mana effect is empty for " + sa.getHostCard().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (final Player player : tgtPlayers) {
|
abMana.produceMana(GameActionUtil.generatedMana(sa), p, sa);
|
||||||
abMana.produceMana(GameActionUtil.generatedMana(sa), player, sa);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only clear express choice after mana has been produced
|
// Only clear express choice after mana has been produced
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import forge.game.spellability.SpellAbility;
|
|||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -29,8 +28,7 @@ public class ManaReflectedEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
final Collection<String> colors = CardUtil.getReflectableManaColors(sa);
|
final Collection<String> colors = CardUtil.getReflectableManaColors(sa);
|
||||||
|
|
||||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
for (final Player player : getTargetPlayers(sa)) {
|
||||||
for (final Player player : tgtPlayers) {
|
|
||||||
final String generated = generatedReflectedMana(sa, colors, player);
|
final String generated = generatedReflectedMana(sa, colors, player);
|
||||||
ma.produceMana(generated, player, sa);
|
ma.produceMana(generated, player, sa);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -149,14 +148,13 @@ public class PlayEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (sa.hasParam("ValidSA")) {
|
if (sa.hasParam("ValidSA")) {
|
||||||
final String valid[] = {sa.getParam("ValidSA")};
|
final String valid[] = {sa.getParam("ValidSA")};
|
||||||
final Iterator<Card> itr = tgtCards.iterator();
|
List<Card> toRemove = Lists.newArrayList();
|
||||||
while (itr.hasNext()) {
|
for (Card c : tgtCards) {
|
||||||
final Card c = itr.next();
|
if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, controller), SpellAbilityPredicates.isValid(valid, controller , c, sa))) {
|
||||||
final List<SpellAbility> validSA = Lists.newArrayList(Iterables.filter(AbilityUtils.getBasicSpellsFromPlayEffect(c, controller), SpellAbilityPredicates.isValid(valid, controller , c, sa)));
|
toRemove.add(c);
|
||||||
if (validSA.size() == 0) {
|
|
||||||
itr.remove();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tgtCards.removeAll(toRemove);
|
||||||
if (tgtCards.isEmpty()) {
|
if (tgtCards.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -260,13 +258,8 @@ public class PlayEffect extends SpellAbilityEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean noManaCost = sa.hasParam("WithoutManaCost");
|
if (sa.hasParam("WithoutManaCost")) {
|
||||||
if (noManaCost) {
|
|
||||||
tgtSA = tgtSA.copyWithNoManaCost();
|
tgtSA = tgtSA.copyWithNoManaCost();
|
||||||
// FIXME: a hack to get cards like Detonate only allow legal targets when cast without paying mana cost (with X=0).
|
|
||||||
if (tgtSA.hasParam("ValidTgtsWithoutManaCost")) {
|
|
||||||
tgtSA.getTargetRestrictions().changeValidTargets(tgtSA.getParam("ValidTgtsWithoutManaCost").split(","));
|
|
||||||
}
|
|
||||||
} else if (sa.hasParam("PlayCost")) {
|
} else if (sa.hasParam("PlayCost")) {
|
||||||
Cost abCost;
|
Cost abCost;
|
||||||
if ("ManaCost".equals(sa.getParam("PlayCost"))) {
|
if ("ManaCost".equals(sa.getParam("PlayCost"))) {
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import forge.game.ability.AbilityKey;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
|
import forge.game.GameLogEntryType;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.replacement.ReplacementResult;
|
import forge.game.replacement.ReplacementResult;
|
||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
public class ReplaceDamageEffect extends SpellAbilityEffect {
|
public class ReplaceDamageEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -58,6 +60,12 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
params.put(AbilityKey.DamageAmount, dmg);
|
params.put(AbilityKey.DamageAmount, dmg);
|
||||||
|
|
||||||
|
// need to log Updated events there, or the log is wrong order
|
||||||
|
String message = sa.getReplacementEffect().toString();
|
||||||
|
if ( !StringUtils.isEmpty(message)) {
|
||||||
|
message = TextUtil.fastReplace(message, "CARDNAME", card.getName());
|
||||||
|
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
|
||||||
|
}
|
||||||
|
|
||||||
//try to call replacementHandler with new Params
|
//try to call replacementHandler with new Params
|
||||||
ReplacementResult result = game.getReplacementHandler().run(event, params);
|
ReplacementResult result = game.getReplacementHandler().run(event, params);
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ package forge.game.ability.effects;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
|
import forge.game.GameLogEntryType;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
@@ -16,6 +19,7 @@ import forge.game.player.Player;
|
|||||||
import forge.game.replacement.ReplacementResult;
|
import forge.game.replacement.ReplacementResult;
|
||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
public class ReplaceEffect extends SpellAbilityEffect {
|
public class ReplaceEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
@@ -61,6 +65,13 @@ public class ReplaceEffect extends SpellAbilityEffect {
|
|||||||
params.put(AbilityKey.EffectOnly, true);
|
params.put(AbilityKey.EffectOnly, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// need to log Updated events there, or the log is wrong order
|
||||||
|
String message = sa.getReplacementEffect().toString();
|
||||||
|
if ( !StringUtils.isEmpty(message)) {
|
||||||
|
message = TextUtil.fastReplace(message, "CARDNAME", card.getName());
|
||||||
|
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
|
||||||
|
}
|
||||||
|
|
||||||
//try to call replacementHandler with new Params
|
//try to call replacementHandler with new Params
|
||||||
ReplacementResult result = game.getReplacementHandler().run(retype, params);
|
ReplacementResult result = game.getReplacementHandler().run(retype, params);
|
||||||
switch (result) {
|
switch (result) {
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import forge.card.ColorSet;
|
||||||
|
import forge.card.MagicColor;
|
||||||
|
import forge.game.Game;
|
||||||
|
import forge.game.GameLogEntryType;
|
||||||
|
import forge.game.ability.AbilityKey;
|
||||||
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.replacement.ReplacementResult;
|
||||||
|
import forge.game.replacement.ReplacementType;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.util.TextUtil;
|
||||||
|
|
||||||
|
public class ReplaceManaEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolve(SpellAbility sa) {
|
||||||
|
final Card card = sa.getHostCard();
|
||||||
|
final Player player = sa.getActivatingPlayer();
|
||||||
|
final Game game = card.getGame();
|
||||||
|
|
||||||
|
// outside of Replacement Effect, unwanted result
|
||||||
|
if (!sa.isReplacementAbility()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ReplacementType event = sa.getReplacementEffect().getMode();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<AbilityKey, Object> originalParams = (Map<AbilityKey, Object>) sa.getReplacingObject(AbilityKey.OriginalParams);
|
||||||
|
Map<AbilityKey, Object> params = Maps.newHashMap(originalParams);
|
||||||
|
|
||||||
|
String replaced = (String)sa.getReplacingObject(AbilityKey.Mana);
|
||||||
|
if (sa.hasParam("ReplaceMana")) {
|
||||||
|
// replace type and amount
|
||||||
|
replaced = sa.getParam("ReplaceMana");
|
||||||
|
if ("Any".equals(replaced)) {
|
||||||
|
byte rs = MagicColor.GREEN;
|
||||||
|
rs = player.getController().chooseColor("Choose a color", sa, ColorSet.ALL_COLORS);
|
||||||
|
replaced = MagicColor.toShortString(rs);
|
||||||
|
}
|
||||||
|
} else if (sa.hasParam("ReplaceType")) {
|
||||||
|
// replace color and colorless
|
||||||
|
String color = sa.getParam("ReplaceType");
|
||||||
|
if ("Any".equals(color)) {
|
||||||
|
byte rs = MagicColor.GREEN;
|
||||||
|
rs = player.getController().chooseColor("Choose a color", sa, ColorSet.ALL_COLORS);
|
||||||
|
color = MagicColor.toShortString(rs);
|
||||||
|
}
|
||||||
|
for (byte c : MagicColor.WUBRGC) {
|
||||||
|
String s = MagicColor.toShortString(c);
|
||||||
|
replaced = replaced.replace(s, color);
|
||||||
|
}
|
||||||
|
} else if (sa.hasParam("ReplaceColor")) {
|
||||||
|
// replace color
|
||||||
|
String color = sa.getParam("ReplaceColor");
|
||||||
|
if ("Chosen".equals(color)) {
|
||||||
|
if (card.hasChosenColor()) {
|
||||||
|
color = MagicColor.toShortString(card.getChosenColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sa.hasParam("ReplaceOnly")) {
|
||||||
|
replaced = replaced.replace(sa.getParam("ReplaceOnly"), color);
|
||||||
|
} else {
|
||||||
|
for (byte c : MagicColor.WUBRG) {
|
||||||
|
String s = MagicColor.toShortString(c);
|
||||||
|
replaced = replaced.replace(s, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (sa.hasParam("ReplaceAmount")) {
|
||||||
|
// replace amount = multiples
|
||||||
|
replaced = StringUtils.repeat(replaced, " ", Integer.valueOf(sa.getParam("ReplaceAmount")));
|
||||||
|
}
|
||||||
|
params.put(AbilityKey.Mana, replaced);
|
||||||
|
|
||||||
|
// need to log Updated events there, or the log is wrong order
|
||||||
|
String message = sa.getReplacementEffect().toString();
|
||||||
|
if ( !StringUtils.isEmpty(message)) {
|
||||||
|
message = TextUtil.fastReplace(message, "CARDNAME", card.getName());
|
||||||
|
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
//try to call replacementHandler with new Params
|
||||||
|
ReplacementResult result = game.getReplacementHandler().run(event, params);
|
||||||
|
switch (result) {
|
||||||
|
case NotReplaced:
|
||||||
|
case Updated: {
|
||||||
|
for (Map.Entry<AbilityKey, Object> e : params.entrySet()) {
|
||||||
|
originalParams.put(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
// effect was updated
|
||||||
|
originalParams.put(AbilityKey.ReplacementResult, ReplacementResult.Updated);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// effect was replaced with something else
|
||||||
|
originalParams.put(AbilityKey.ReplacementResult, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
|
|||||||
if (card.getType().hasStringType("Effect") && prevent <= 0) {
|
if (card.getType().hasStringType("Effect") && prevent <= 0) {
|
||||||
game.getAction().exile(card, null);
|
game.getAction().exile(card, null);
|
||||||
} else if (!StringUtils.isNumeric(varValue)) {
|
} else if (!StringUtils.isNumeric(varValue)) {
|
||||||
card.setSVar(varValue, "Number$" + prevent);
|
sa.setSVar(varValue, "Number$" + prevent);
|
||||||
}
|
}
|
||||||
|
|
||||||
Card sourceLKI = (Card) sa.getReplacingObject(AbilityKey.Source);
|
Card sourceLKI = (Card) sa.getReplacingObject(AbilityKey.Source);
|
||||||
|
|||||||
@@ -625,10 +625,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
|
|
||||||
public Card manifest(Player p, SpellAbility sa) {
|
public Card manifest(Player p, SpellAbility sa) {
|
||||||
// Turn Face Down (even if it's DFC).
|
// Turn Face Down (even if it's DFC).
|
||||||
ManaCost cost = getState(CardStateName.Original).getManaCost();
|
|
||||||
|
|
||||||
boolean isCreature = isCreature();
|
|
||||||
|
|
||||||
// Sometimes cards are manifested while already being face down
|
// Sometimes cards are manifested while already being face down
|
||||||
if (!turnFaceDown(true) && !isFaceDown()) {
|
if (!turnFaceDown(true) && !isFaceDown()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -643,11 +639,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
setManifested(true);
|
setManifested(true);
|
||||||
|
|
||||||
Card c = game.getAction().moveToPlay(this, p, sa);
|
Card c = game.getAction().moveToPlay(this, p, sa);
|
||||||
|
if (c.isInPlay()) {
|
||||||
// Add manifest demorph static ability for creatures
|
c.setManifested(true);
|
||||||
if (c.isManifested() && isCreature && !cost.isNoCost()) {
|
c.turnFaceDown(true);
|
||||||
// Add Manifest to original State
|
|
||||||
c.getState(CardStateName.Original).addSpellAbility(CardFactoryUtil.abilityManifestFaceUp(c, cost));
|
|
||||||
c.updateStateForView();
|
c.updateStateForView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2491,7 +2485,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
if (ab.getApi() == ApiType.ManaReflected) {
|
if (ab.getApi() == ApiType.ManaReflected) {
|
||||||
colors.addAll(CardUtil.getReflectableManaColors(ab));
|
colors.addAll(CardUtil.getReflectableManaColors(ab));
|
||||||
} else {
|
} else {
|
||||||
colors = CardUtil.canProduce(6, ab.getManaPart(), colors);
|
colors = CardUtil.canProduce(6, ab, colors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2502,7 +2496,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (mana.getManaPart().canProduce(MagicColor.toShortString(s))) {
|
if (mana.canProduce(MagicColor.toShortString(s))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5450,15 +5444,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
// Note: This should only be called after state has been set to CardStateName.FaceDown,
|
// Note: This should only be called after state has been set to CardStateName.FaceDown,
|
||||||
// so the below call should be valid since the state should have been created already.
|
// so the below call should be valid since the state should have been created already.
|
||||||
getState(CardStateName.FaceDown).setImageKey(ImageKeys.getTokenKey(image));
|
getState(CardStateName.FaceDown).setImageKey(ImageKeys.getTokenKey(image));
|
||||||
if (!manifested) {
|
|
||||||
// remove Manifest Up abilities from Original State
|
|
||||||
CardState original = getState(CardStateName.Original);
|
|
||||||
for (SpellAbility sa : original.getNonManaAbilities()) {
|
|
||||||
if (sa.isManifestUp()) {
|
|
||||||
original.removeSpellAbility(sa);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isForetold() {
|
public final boolean isForetold() {
|
||||||
@@ -6090,21 +6075,20 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
|
|
||||||
public boolean hasETBTrigger(final boolean drawbackOnly) {
|
public boolean hasETBTrigger(final boolean drawbackOnly) {
|
||||||
for (final Trigger tr : getTriggers()) {
|
for (final Trigger tr : getTriggers()) {
|
||||||
final Map<String, String> params = tr.getMapParams();
|
|
||||||
if (tr.getMode() != TriggerType.ChangesZone) {
|
if (tr.getMode() != TriggerType.ChangesZone) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.get("Destination").equals(ZoneType.Battlefield.toString())) {
|
if (!tr.getParam("Destination").equals(ZoneType.Battlefield.toString())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("ValidCard") && !params.get("ValidCard").contains("Self")) {
|
if (tr.hasParam("ValidCard") && !tr.getParam("ValidCard").contains("Self")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (drawbackOnly && params.containsKey("Execute")){
|
if (drawbackOnly) {
|
||||||
String exec = this.getSVar(params.get("Execute"));
|
SpellAbility sa = tr.ensureAbility();
|
||||||
if (exec.contains("AB$")) {
|
if (sa == null || sa.isActivatedAbility()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6332,6 +6316,14 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isInPlay() && isFaceDown() && isManifested()) {
|
||||||
|
CardState oState = getState(CardStateName.Original);
|
||||||
|
ManaCost cost = oState.getManaCost();
|
||||||
|
if (oState.getType().isCreature()) {
|
||||||
|
abilities.add(CardFactoryUtil.abilityManifestFaceUp(this, cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final Collection<SpellAbility> toRemove = Lists.newArrayListWithCapacity(abilities.size());
|
final Collection<SpellAbility> toRemove = Lists.newArrayListWithCapacity(abilities.size());
|
||||||
for (final SpellAbility sa : abilities) {
|
for (final SpellAbility sa : abilities) {
|
||||||
sa.setActivatingPlayer(player);
|
sa.setActivatingPlayer(player);
|
||||||
|
|||||||
@@ -117,10 +117,9 @@ public class CardFactory {
|
|||||||
* which wouldn't ordinarily get set during a simple Card.copy() call.
|
* which wouldn't ordinarily get set during a simple Card.copy() call.
|
||||||
* </p>
|
* </p>
|
||||||
* */
|
* */
|
||||||
private final static Card copySpellHost(final SpellAbility sourceSA, final SpellAbility targetSA){
|
private final static Card copySpellHost(final SpellAbility sourceSA, final SpellAbility targetSA, Player controller) {
|
||||||
final Card source = sourceSA.getHostCard();
|
final Card source = sourceSA.getHostCard();
|
||||||
final Card original = targetSA.getHostCard();
|
final Card original = targetSA.getHostCard();
|
||||||
Player controller = sourceSA.getActivatingPlayer();
|
|
||||||
final Card c = copyCard(original, true);
|
final Card c = copyCard(original, true);
|
||||||
|
|
||||||
// change the color of the copy (eg: Fork)
|
// change the color of the copy (eg: Fork)
|
||||||
@@ -168,17 +167,15 @@ public class CardFactory {
|
|||||||
* @param bCopyDetails
|
* @param bCopyDetails
|
||||||
* a boolean.
|
* a boolean.
|
||||||
*/
|
*/
|
||||||
public final static SpellAbility copySpellAbilityAndPossiblyHost(final SpellAbility sourceSA, final SpellAbility targetSA) {
|
public final static SpellAbility copySpellAbilityAndPossiblyHost(final SpellAbility sourceSA, final SpellAbility targetSA, final Player controller) {
|
||||||
Player controller = sourceSA.getActivatingPlayer();
|
|
||||||
|
|
||||||
//it is only necessary to copy the host card if the SpellAbility is a spell, not an ability
|
//it is only necessary to copy the host card if the SpellAbility is a spell, not an ability
|
||||||
final Card c = targetSA.isSpell() ? copySpellHost(sourceSA, targetSA) : targetSA.getHostCard();
|
final Card c = targetSA.isSpell() ? copySpellHost(sourceSA, targetSA, controller) : targetSA.getHostCard();
|
||||||
|
|
||||||
final SpellAbility copySA;
|
final SpellAbility copySA;
|
||||||
if (targetSA.isTrigger() && targetSA.isWrapper()) {
|
if (targetSA.isTrigger() && targetSA.isWrapper()) {
|
||||||
copySA = getCopiedTriggeredAbility((WrappedAbility)targetSA, c);
|
copySA = getCopiedTriggeredAbility((WrappedAbility)targetSA, c, controller);
|
||||||
} else {
|
} else {
|
||||||
copySA = targetSA.copy(c, false);
|
copySA = targetSA.copy(c, controller, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
copySA.setCopied(true);
|
copySA.setCopied(true);
|
||||||
@@ -361,12 +358,12 @@ public class CardFactory {
|
|||||||
// Name first so Senty has the Card name
|
// Name first so Senty has the Card name
|
||||||
c.setName(face.getName());
|
c.setName(face.getName());
|
||||||
|
|
||||||
|
for (Entry<String, String> v : face.getVariables()) c.setSVar(v.getKey(), v.getValue());
|
||||||
|
|
||||||
for (String r : face.getReplacements()) c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true));
|
for (String r : face.getReplacements()) c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true));
|
||||||
for (String s : face.getStaticAbilities()) c.addStaticAbility(s);
|
for (String s : face.getStaticAbilities()) c.addStaticAbility(s);
|
||||||
for (String t : face.getTriggers()) c.addTrigger(TriggerHandler.parseTrigger(t, c, true));
|
for (String t : face.getTriggers()) c.addTrigger(TriggerHandler.parseTrigger(t, c, true));
|
||||||
|
|
||||||
for (Entry<String, String> v : face.getVariables()) c.setSVar(v.getKey(), v.getValue());
|
|
||||||
|
|
||||||
// keywords not before variables
|
// keywords not before variables
|
||||||
c.addIntrinsicKeywords(face.getKeywords(), false);
|
c.addIntrinsicKeywords(face.getKeywords(), false);
|
||||||
|
|
||||||
@@ -555,12 +552,12 @@ public class CardFactory {
|
|||||||
*
|
*
|
||||||
* return a wrapped ability
|
* return a wrapped ability
|
||||||
*/
|
*/
|
||||||
public static SpellAbility getCopiedTriggeredAbility(final WrappedAbility sa, final Card newHost) {
|
public static SpellAbility getCopiedTriggeredAbility(final WrappedAbility sa, final Card newHost, final Player controller) {
|
||||||
if (!sa.isTrigger()) {
|
if (!sa.isTrigger()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WrappedAbility(sa.getTrigger(), sa.getWrappedAbility().copy(newHost, false), sa.getDecider());
|
return new WrappedAbility(sa.getTrigger(), sa.getWrappedAbility().copy(newHost, controller, false), controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CardCloneStates getCloneStates(final Card in, final Card out, final CardTraitBase sa) {
|
public static CardCloneStates getCloneStates(final Card in, final Card out, final CardTraitBase sa) {
|
||||||
@@ -667,6 +664,7 @@ public class CardFactory {
|
|||||||
if (origSVars.containsKey(s)) {
|
if (origSVars.containsKey(s)) {
|
||||||
final String actualTrigger = origSVars.get(s);
|
final String actualTrigger = origSVars.get(s);
|
||||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, out, true);
|
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, out, true);
|
||||||
|
parsedTrigger.setOriginalHost(host);
|
||||||
state.addTrigger(parsedTrigger);
|
state.addTrigger(parsedTrigger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -690,6 +688,7 @@ public class CardFactory {
|
|||||||
if (origSVars.containsKey(s)) {
|
if (origSVars.containsKey(s)) {
|
||||||
final String actualAbility = origSVars.get(s);
|
final String actualAbility = origSVars.get(s);
|
||||||
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, out);
|
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, out);
|
||||||
|
grantedAbility.setOriginalHost(host);
|
||||||
grantedAbility.setIntrinsic(true);
|
grantedAbility.setIntrinsic(true);
|
||||||
state.addSpellAbility(grantedAbility);
|
state.addSpellAbility(grantedAbility);
|
||||||
}
|
}
|
||||||
@@ -703,6 +702,7 @@ public class CardFactory {
|
|||||||
if (origSVars.containsKey(s)) {
|
if (origSVars.containsKey(s)) {
|
||||||
final String actualStatic = origSVars.get(s);
|
final String actualStatic = origSVars.get(s);
|
||||||
final StaticAbility grantedStatic = new StaticAbility(actualStatic, out);
|
final StaticAbility grantedStatic = new StaticAbility(actualStatic, out);
|
||||||
|
grantedStatic.setOriginalHost(host);
|
||||||
grantedStatic.setIntrinsic(true);
|
grantedStatic.setIntrinsic(true);
|
||||||
state.addStaticAbility(grantedStatic);
|
state.addStaticAbility(grantedStatic);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ public class CardFactoryUtil {
|
|||||||
// Cost need to be set later
|
// Cost need to be set later
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("ST$ SetState | Cost$ 0 | CostDesc$ Unmanifest ").append(costDesc);
|
sb.append("ST$ SetState | Cost$ 0 | CostDesc$ Unmanifest ").append(costDesc);
|
||||||
sb.append(" | ManifestUp$ True | Secondary$ True | IsPresent$ Card.Self+faceDown+manifested");
|
sb.append(" | ManifestUp$ True | Secondary$ True | PresentDefined$ Self | IsPresent$ Card.faceDown+manifested");
|
||||||
sb.append(" | Mode$ TurnFace | SpellDescription$ (Turn this face up any time for its mana cost.)");
|
sb.append(" | Mode$ TurnFace | SpellDescription$ (Turn this face up any time for its mana cost.)");
|
||||||
|
|
||||||
final SpellAbility manifestUp = AbilityFactory.getAbility(sb.toString(), sourceCard);
|
final SpellAbility manifestUp = AbilityFactory.getAbility(sb.toString(), sourceCard);
|
||||||
@@ -1478,7 +1478,7 @@ public class CardFactoryUtil {
|
|||||||
for (Card card : otb) {
|
for (Card card : otb) {
|
||||||
if (!card.isTapped() || !untappedOnly) {
|
if (!card.isTapped() || !untappedOnly) {
|
||||||
for (SpellAbility ma : card.getManaAbilities()) {
|
for (SpellAbility ma : card.getManaAbilities()) {
|
||||||
if (ma.getManaPart().canProduce(MagicColor.toShortString(color))) {
|
if (ma.canProduce(MagicColor.toShortString(color))) {
|
||||||
uniqueColors++;
|
uniqueColors++;
|
||||||
continue outer;
|
continue outer;
|
||||||
}
|
}
|
||||||
@@ -2230,7 +2230,7 @@ public class CardFactoryUtil {
|
|||||||
final String abStringAfflict = "DB$ LoseLife | Defined$ TriggeredDefendingPlayer" +
|
final String abStringAfflict = "DB$ LoseLife | Defined$ TriggeredDefendingPlayer" +
|
||||||
" | LifeAmount$ " + n;
|
" | LifeAmount$ " + n;
|
||||||
|
|
||||||
final Trigger afflictTrigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
|
final Trigger afflictTrigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic, null);
|
||||||
afflictTrigger.setOverridingAbility(AbilityFactory.getAbility(abStringAfflict, card));
|
afflictTrigger.setOverridingAbility(AbilityFactory.getAbility(abStringAfflict, card));
|
||||||
|
|
||||||
inst.addTrigger(afflictTrigger);
|
inst.addTrigger(afflictTrigger);
|
||||||
|
|||||||
@@ -648,9 +648,10 @@ public class CardProperty {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (property.startsWith("DamagedBy")) {
|
} else if (property.startsWith("DamagedBy")) {
|
||||||
if ((property.endsWith("Source") || property.equals("DamagedBy")) &&
|
if (property.endsWith("Source") || property.equals("DamagedBy")) {
|
||||||
!card.getReceivedDamageFromThisTurn().containsKey(source)) {
|
if (!card.getReceivedDamageFromThisTurn().containsKey(source)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
String prop = property.substring("DamagedBy".length());
|
String prop = property.substring("DamagedBy".length());
|
||||||
|
|
||||||
@@ -911,7 +912,7 @@ public class CardProperty {
|
|||||||
} else if (property.startsWith("canProduceManaColor")) {
|
} else if (property.startsWith("canProduceManaColor")) {
|
||||||
final String color = property.split("canProduceManaColor ")[1];
|
final String color = property.split("canProduceManaColor ")[1];
|
||||||
for (SpellAbility ma : card.getManaAbilities()) {
|
for (SpellAbility ma : card.getManaAbilities()) {
|
||||||
if (ma.getManaPart().canProduce(MagicColor.toShortString(color))) {
|
if (ma.canProduce(MagicColor.toShortString(color))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -391,7 +391,6 @@ public final class CardUtil {
|
|||||||
|
|
||||||
final String colorOrType = sa.getParam("ColorOrType");
|
final String colorOrType = sa.getParam("ColorOrType");
|
||||||
// currently Color or Type, Type is colors + colorless
|
// currently Color or Type, Type is colors + colorless
|
||||||
final String validCard = sa.getParam("Valid");
|
|
||||||
final String reflectProperty = sa.getParam("ReflectProperty");
|
final String reflectProperty = sa.getParam("ReflectProperty");
|
||||||
// Produce (Reflecting Pool) or Is (Meteor Crater)
|
// Produce (Reflecting Pool) or Is (Meteor Crater)
|
||||||
|
|
||||||
@@ -400,8 +399,10 @@ public final class CardUtil {
|
|||||||
maxChoices++;
|
maxChoices++;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection cards = null;
|
CardCollection cards;
|
||||||
|
|
||||||
|
if (sa.hasParam("Valid")) {
|
||||||
|
final String validCard = sa.getParam("Valid");
|
||||||
// Reuse AF_Defined in a slightly different way
|
// Reuse AF_Defined in a slightly different way
|
||||||
if (validCard.startsWith("Defined.")) {
|
if (validCard.startsWith("Defined.")) {
|
||||||
cards = AbilityUtils.getDefinedCards(card, TextUtil.fastReplace(validCard, "Defined.", ""), abMana);
|
cards = AbilityUtils.getDefinedCards(card, TextUtil.fastReplace(validCard, "Defined.", ""), abMana);
|
||||||
@@ -414,14 +415,14 @@ public final class CardUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove anything cards that is already in parents
|
// remove anything cards that is already in parents
|
||||||
for (final Card p : parents) {
|
cards.removeAll(parents);
|
||||||
cards.remove(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((cards.size() == 0) && !reflectProperty.equals("Produced")) {
|
if (cards.isEmpty()) {
|
||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
cards = new CardCollection();
|
||||||
|
}
|
||||||
if (reflectProperty.equals("Is")) { // Meteor Crater
|
if (reflectProperty.equals("Is")) { // Meteor Crater
|
||||||
for (final Card card1 : cards) {
|
for (final Card card1 : cards) {
|
||||||
// For each card, go through all the colors and if the card is that color, add
|
// For each card, go through all the colors and if the card is that color, add
|
||||||
@@ -436,7 +437,7 @@ public final class CardUtil {
|
|||||||
}
|
}
|
||||||
} else if (reflectProperty.equals("Produced")) {
|
} else if (reflectProperty.equals("Produced")) {
|
||||||
// Why is this name so similar to the one below?
|
// Why is this name so similar to the one below?
|
||||||
final String producedColors = abMana instanceof AbilitySub ? (String) abMana.getRootAbility().getTriggeringObject(AbilityKey.Produced) : (String) abMana.getTriggeringObject(AbilityKey.Produced);
|
final String producedColors = (String) abMana.getRootAbility().getTriggeringObject(AbilityKey.Produced);
|
||||||
for (final String col : MagicColor.Constant.ONLY_COLORS) {
|
for (final String col : MagicColor.Constant.ONLY_COLORS) {
|
||||||
final String s = MagicColor.toShortString(col);
|
final String s = MagicColor.toShortString(col);
|
||||||
if (producedColors.contains(s)) {
|
if (producedColors.contains(s)) {
|
||||||
@@ -469,7 +470,7 @@ public final class CardUtil {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
colors = canProduce(maxChoices, ab.getManaPart(), colors);
|
colors = canProduce(maxChoices, ab, colors);
|
||||||
if (!parents.contains(ab.getHostCard())) {
|
if (!parents.contains(ab.getHostCard())) {
|
||||||
parents.add(ab.getHostCard());
|
parents.add(ab.getHostCard());
|
||||||
}
|
}
|
||||||
@@ -486,19 +487,18 @@ public final class CardUtil {
|
|||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<String> canProduce(final int maxChoices, final AbilityManaPart ab,
|
public static Set<String> canProduce(final int maxChoices, final SpellAbility sa,
|
||||||
final Set<String> colors) {
|
final Set<String> colors) {
|
||||||
if (ab == null) {
|
if (sa == null) {
|
||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
for (final String col : MagicColor.Constant.ONLY_COLORS) {
|
for (final String col : MagicColor.Constant.ONLY_COLORS) {
|
||||||
final String s = MagicColor.toShortString(col);
|
if (sa.canProduce(MagicColor.toShortString(col))) {
|
||||||
if (ab.canProduce(s)) {
|
|
||||||
colors.add(col);
|
colors.add(col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxChoices == 6 && ab.canProduce("C")) {
|
if (maxChoices == 6 && sa.canProduce("C")) {
|
||||||
colors.add(MagicColor.Constant.COLORLESS);
|
colors.add(MagicColor.Constant.COLORLESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ package forge.game.mana;
|
|||||||
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 com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.card.mana.IParserManaCost;
|
import forge.card.mana.IParserManaCost;
|
||||||
@@ -105,11 +107,16 @@ public class ManaCostBeingPaid {
|
|||||||
xCount = copy.xCount;
|
xCount = copy.xCount;
|
||||||
totalCount = copy.totalCount;
|
totalCount = copy.totalCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{x=" + xCount + " total=" + totalCount + "}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// holds Mana_Part objects
|
// holds Mana_Part objects
|
||||||
// ManaPartColor is stored before ManaPartGeneric
|
// ManaPartColor is stored before ManaPartGeneric
|
||||||
private final Map<ManaCostShard, ShardCount> unpaidShards = new HashMap<>();
|
private final Map<ManaCostShard, ShardCount> unpaidShards = Maps.newHashMap();
|
||||||
private Map<String, Integer> xManaCostPaidByColor;
|
private Map<String, Integer> xManaCostPaidByColor;
|
||||||
private final String sourceRestriction;
|
private final String sourceRestriction;
|
||||||
private byte sunburstMap = 0;
|
private byte sunburstMap = 0;
|
||||||
@@ -124,7 +131,7 @@ public class ManaCostBeingPaid {
|
|||||||
unpaidShards.put(m.getKey(), new ShardCount(m.getValue()));
|
unpaidShards.put(m.getKey(), new ShardCount(m.getValue()));
|
||||||
}
|
}
|
||||||
if (manaCostBeingPaid.xManaCostPaidByColor != null) {
|
if (manaCostBeingPaid.xManaCostPaidByColor != null) {
|
||||||
xManaCostPaidByColor = new HashMap<>(manaCostBeingPaid.xManaCostPaidByColor);
|
xManaCostPaidByColor = Maps.newHashMap(manaCostBeingPaid.xManaCostPaidByColor);
|
||||||
}
|
}
|
||||||
sourceRestriction = manaCostBeingPaid.sourceRestriction;
|
sourceRestriction = manaCostBeingPaid.sourceRestriction;
|
||||||
sunburstMap = manaCostBeingPaid.sunburstMap;
|
sunburstMap = manaCostBeingPaid.sunburstMap;
|
||||||
@@ -503,7 +510,7 @@ public class ManaCostBeingPaid {
|
|||||||
sc.xCount--;
|
sc.xCount--;
|
||||||
String color = MagicColor.toShortString(colorMask);
|
String color = MagicColor.toShortString(colorMask);
|
||||||
if (xManaCostPaidByColor == null) {
|
if (xManaCostPaidByColor == null) {
|
||||||
xManaCostPaidByColor = new HashMap<>();
|
xManaCostPaidByColor = Maps.newHashMap();
|
||||||
}
|
}
|
||||||
Integer xColor = xManaCostPaidByColor.get(color);
|
Integer xColor = xManaCostPaidByColor.get(color);
|
||||||
if (xColor == null) {
|
if (xColor == null) {
|
||||||
@@ -602,19 +609,7 @@ public class ManaCostBeingPaid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int nGeneric = getGenericManaAmount();
|
int nGeneric = getGenericManaAmount();
|
||||||
List<ManaCostShard> shards = new ArrayList<>(unpaidShards.keySet());
|
List<ManaCostShard> shards = Lists.newArrayList(unpaidShards.keySet());
|
||||||
|
|
||||||
// TODO Fix this. Should we really be changing Shards here?
|
|
||||||
if (false && pool != null) { //replace shards with generic mana if they can be paid with any color mana
|
|
||||||
for (int i = 0; i < shards.size(); i++) {
|
|
||||||
ManaCostShard shard = shards.get(i);
|
|
||||||
if (shard != ManaCostShard.GENERIC && pool.getPossibleColorUses(shard.getColorMask()) == ManaAtom.ALL_MANA_TYPES) {
|
|
||||||
nGeneric += unpaidShards.get(shard).totalCount;
|
|
||||||
shards.remove(i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nGeneric > 0) {
|
if (nGeneric > 0) {
|
||||||
if (nGeneric <= 20) {
|
if (nGeneric <= 20) {
|
||||||
|
|||||||
@@ -166,26 +166,30 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
|||||||
owner.updateManaForView();
|
owner.updateManaForView();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeMana(final Mana mana) {
|
private boolean removeMana(final Mana mana) {
|
||||||
Collection<Mana> cm = floatingMana.get(mana.getColor());
|
if (floatingMana.remove(mana.getColor(), mana)) {
|
||||||
if (cm.remove(mana)) {
|
|
||||||
owner.updateManaForView();
|
owner.updateManaForView();
|
||||||
owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Removed, mana));
|
owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Removed, mana));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void payManaFromAbility(final SpellAbility saPaidFor, ManaCostBeingPaid manaCost, final SpellAbility saPayment) {
|
public final void payManaFromAbility(final SpellAbility saPaidFor, ManaCostBeingPaid manaCost, final SpellAbility saPayment) {
|
||||||
// Mana restriction must be checked before this method is called
|
// Mana restriction must be checked before this method is called
|
||||||
final List<SpellAbility> paidAbs = saPaidFor.getPayingManaAbilities();
|
final List<SpellAbility> paidAbs = saPaidFor.getPayingManaAbilities();
|
||||||
AbilityManaPart abManaPart = saPayment.getManaPartRecursive();
|
|
||||||
|
|
||||||
paidAbs.add(saPayment); // assumes some part on the mana produced by the ability will get used
|
paidAbs.add(saPayment); // assumes some part on the mana produced by the ability will get used
|
||||||
for (final Mana mana : abManaPart.getLastManaProduced()) {
|
|
||||||
|
// need to get all mana from all ManaAbilities of the SpellAbility
|
||||||
|
for (AbilityManaPart mp : saPayment.getAllManaParts()) {
|
||||||
|
for (final Mana mana : mp.getLastManaProduced()) {
|
||||||
if (tryPayCostWithMana(saPaidFor, manaCost, mana, false)) {
|
if (tryPayCostWithMana(saPaidFor, manaCost, mana, false)) {
|
||||||
saPaidFor.getPayingMana().add(0, mana);
|
saPaidFor.getPayingMana().add(0, mana);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean tryPayCostWithColor(byte colorCode, SpellAbility saPaidFor, ManaCostBeingPaid manaCost) {
|
public boolean tryPayCostWithColor(byte colorCode, SpellAbility saPaidFor, ManaCostBeingPaid manaCost) {
|
||||||
Mana manaFound = null;
|
Mana manaFound = null;
|
||||||
@@ -216,8 +220,12 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
|||||||
if (!manaCost.isNeeded(mana, this)) {
|
if (!manaCost.isNeeded(mana, this)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// only pay mana into manaCost when the Mana could be removed from the Mana pool
|
||||||
|
// if the mana wasn't in the mana pool then something is wrong
|
||||||
|
if (!removeMana(mana)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
manaCost.payMana(mana, this);
|
manaCost.payMana(mana, this);
|
||||||
removeMana(mana);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3539,4 +3539,11 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
public void resetCycledThisTurn() {
|
public void resetCycledThisTurn() {
|
||||||
cycledThisTurn = 0;
|
cycledThisTurn = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasUrzaLands() {
|
||||||
|
final CardCollectionView landsControlled = getCardsIn(ZoneType.Battlefield);
|
||||||
|
return Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Mine")))
|
||||||
|
&& Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Power-Plant")))
|
||||||
|
&& Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Tower")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class PlayerController {
|
|||||||
public abstract Integer announceRequirements(SpellAbility ability, String announce);
|
public abstract Integer announceRequirements(SpellAbility ability, String announce);
|
||||||
public abstract CardCollectionView choosePermanentsToSacrifice(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
|
public abstract CardCollectionView choosePermanentsToSacrifice(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
|
||||||
public abstract CardCollectionView choosePermanentsToDestroy(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
|
public abstract CardCollectionView choosePermanentsToDestroy(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
|
||||||
public abstract TargetChoices chooseNewTargetsFor(SpellAbility ability);
|
public abstract TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate<GameObject> filter, boolean optional);
|
||||||
public abstract boolean chooseTargetsFor(SpellAbility currentAbility); // this is bad a function for it assigns targets to sa inside its body
|
public abstract boolean chooseTargetsFor(SpellAbility currentAbility); // this is bad a function for it assigns targets to sa inside its body
|
||||||
|
|
||||||
// Specify a target of a spell (Spellskite)
|
// Specify a target of a spell (Spellskite)
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ public class ReplaceProduceMana extends ReplacementEffect {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
||||||
//Check for tapping
|
|
||||||
if (!hasParam("NoTapCheck")) {
|
if (hasParam("ValidAbility")) {
|
||||||
final SpellAbility manaAbility = (SpellAbility) runParams.get(AbilityKey.AbilityMana);
|
final SpellAbility manaAbility = (SpellAbility) runParams.get(AbilityKey.AbilityMana);
|
||||||
if (manaAbility == null || !manaAbility.getRootAbility().getPayCosts().hasTapCost()) {
|
if (!matchesValid(manaAbility, getParam("ValidAbility").split(","), getHostCard())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,15 +43,23 @@ public class ReplaceProduceMana extends ReplacementEffect {
|
|||||||
String full = getParam("ManaAmount");
|
String full = getParam("ManaAmount");
|
||||||
String operator = full.substring(0, 2);
|
String operator = full.substring(0, 2);
|
||||||
String operand = full.substring(2);
|
String operand = full.substring(2);
|
||||||
|
|
||||||
int intoperand = AbilityUtils.calculateAmount(getHostCard(), operand, this);
|
int intoperand = AbilityUtils.calculateAmount(getHostCard(), operand, this);
|
||||||
|
|
||||||
int manaAmount = StringUtils.countMatches((String) runParams.get(AbilityKey.Mana), " ") + 1;
|
int manaAmount = StringUtils.countMatches((String) runParams.get(AbilityKey.Mana), " ") + 1;
|
||||||
if (!Expressions.compare(manaAmount, operator, intoperand)) {
|
if (!Expressions.compare(manaAmount, operator, intoperand)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasParam("ValidPlayer")) {
|
||||||
|
if (!matchesValid(runParams.get(AbilityKey.Player), getParam("ValidPlayer").split(","), getHostCard())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hasParam("ValidCard")) {
|
if (hasParam("ValidCard")) {
|
||||||
if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), this.getHostCard())) {
|
if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), getHostCard())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,4 +68,7 @@ public class ReplaceProduceMana extends ReplacementEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
|
||||||
|
sa.setReplacingObject(AbilityKey.Mana, runParams.get(AbilityKey.Mana));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package forge.game.replacement;
|
|||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.TriggerReplacementBase;
|
import forge.game.TriggerReplacementBase;
|
||||||
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
@@ -271,4 +272,13 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
|
|||||||
void setMode(ReplacementType mode) {
|
void setMode(ReplacementType mode) {
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SpellAbility ensureAbility() {
|
||||||
|
SpellAbility sa = getOverridingAbility();
|
||||||
|
if (sa == null && hasParam("ReplaceWith")) {
|
||||||
|
sa = AbilityFactory.getAbility(getHostCard(), getParam("ReplaceWith"));
|
||||||
|
setOverridingAbility(sa);
|
||||||
|
}
|
||||||
|
return sa;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game.replacement;
|
package forge.game.replacement;
|
||||||
|
|
||||||
import forge.card.MagicColor;
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameLogEntryType;
|
import forge.game.GameLogEntryType;
|
||||||
|
import forge.game.IHasSVars;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
@@ -259,12 +259,17 @@ public class ReplacementHandler {
|
|||||||
chosenRE.setHasRun(false);
|
chosenRE.setHasRun(false);
|
||||||
hasRun.remove(chosenRE);
|
hasRun.remove(chosenRE);
|
||||||
chosenRE.setOtherChoices(null);
|
chosenRE.setOtherChoices(null);
|
||||||
|
|
||||||
|
// Updated Replacements need to be logged elsewhere because its otherwise in the wrong order
|
||||||
|
if (res != ReplacementResult.Updated) {
|
||||||
String message = chosenRE.getDescription();
|
String message = chosenRE.getDescription();
|
||||||
if ( !StringUtils.isEmpty(message))
|
if ( !StringUtils.isEmpty(message))
|
||||||
if (chosenRE.getHostCard() != null) {
|
if (chosenRE.getHostCard() != null) {
|
||||||
message = TextUtil.fastReplace(message, "CARDNAME", chosenRE.getHostCard().getName());
|
message = TextUtil.fastReplace(message, "CARDNAME", chosenRE.getHostCard().getName());
|
||||||
}
|
}
|
||||||
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
|
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,26 +349,12 @@ public class ReplacementHandler {
|
|||||||
|
|
||||||
Player player = host.getController();
|
Player player = host.getController();
|
||||||
|
|
||||||
if (mapParams.containsKey("ManaReplacement")) {
|
|
||||||
final SpellAbility manaAb = (SpellAbility) runParams.get(AbilityKey.AbilityMana);
|
|
||||||
final Player player1 = (Player) runParams.get(AbilityKey.Player);
|
|
||||||
final String rep = (String) runParams.get(AbilityKey.Mana);
|
|
||||||
// Replaced mana type
|
|
||||||
final Card repHost = host;
|
|
||||||
String repType = repHost.getSVar(mapParams.get("ManaReplacement"));
|
|
||||||
if (repType.contains("Chosen") && repHost.hasChosenColor()) {
|
|
||||||
repType = TextUtil.fastReplace(repType, "Chosen", MagicColor.toShortString(repHost.getChosenColor()));
|
|
||||||
}
|
|
||||||
manaAb.getManaPart().setManaReplaceType(repType);
|
|
||||||
manaAb.getManaPart().produceMana(rep, player1, manaAb);
|
|
||||||
} else {
|
|
||||||
player.getController().playSpellAbilityNoStack(effectSA, true);
|
player.getController().playSpellAbilityNoStack(effectSA, true);
|
||||||
// if the spellability is a replace effect then its some new logic
|
// if the spellability is a replace effect then its some new logic
|
||||||
// if ReplacementResult is set in run params use that instead
|
// if ReplacementResult is set in run params use that instead
|
||||||
if (runParams.containsKey(AbilityKey.ReplacementResult)) {
|
if (runParams.containsKey(AbilityKey.ReplacementResult)) {
|
||||||
return (ReplacementResult) runParams.get(AbilityKey.ReplacementResult);
|
return (ReplacementResult) runParams.get(AbilityKey.ReplacementResult);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ReplacementResult.Replaced;
|
return ReplacementResult.Replaced;
|
||||||
}
|
}
|
||||||
@@ -380,7 +371,10 @@ public class ReplacementHandler {
|
|||||||
* @return A finished instance
|
* @return A finished instance
|
||||||
*/
|
*/
|
||||||
public static ReplacementEffect parseReplacement(final String repParse, final Card host, final boolean intrinsic) {
|
public static ReplacementEffect parseReplacement(final String repParse, final Card host, final boolean intrinsic) {
|
||||||
return ReplacementHandler.parseReplacement(parseParams(repParse), host, intrinsic);
|
return parseReplacement(repParse, host, intrinsic, host);
|
||||||
|
}
|
||||||
|
public static ReplacementEffect parseReplacement(final String repParse, final Card host, final boolean intrinsic, final IHasSVars sVarHolder) {
|
||||||
|
return ReplacementHandler.parseReplacement(parseParams(repParse), host, intrinsic, sVarHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, String> parseParams(final String repParse) {
|
public static Map<String, String> parseParams(final String repParse) {
|
||||||
@@ -398,7 +392,7 @@ public class ReplacementHandler {
|
|||||||
* The card that hosts the replacement effect
|
* The card that hosts the replacement effect
|
||||||
* @return The finished instance
|
* @return The finished instance
|
||||||
*/
|
*/
|
||||||
private static ReplacementEffect parseReplacement(final Map<String, String> mapParams, final Card host, final boolean intrinsic) {
|
private static ReplacementEffect parseReplacement(final Map<String, String> mapParams, final Card host, final boolean intrinsic, final IHasSVars sVarHolder) {
|
||||||
final ReplacementType rt = ReplacementType.smartValueOf(mapParams.get("Event"));
|
final ReplacementType rt = ReplacementType.smartValueOf(mapParams.get("Event"));
|
||||||
ReplacementEffect ret = rt.createReplacement(mapParams, host, intrinsic);
|
ReplacementEffect ret = rt.createReplacement(mapParams, host, intrinsic);
|
||||||
|
|
||||||
@@ -407,6 +401,10 @@ public class ReplacementHandler {
|
|||||||
ret.setActiveZone(EnumSet.copyOf(ZoneType.listValueOf(activeZones)));
|
ret.setActiveZone(EnumSet.copyOf(ZoneType.listValueOf(activeZones)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mapParams.containsKey("ReplaceWith") && sVarHolder != null) {
|
||||||
|
ret.setOverridingAbility(AbilityFactory.getAbility(host, mapParams.get("ReplaceWith"), sVarHolder));
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,18 +18,18 @@
|
|||||||
package forge.game.spellability;
|
package forge.game.spellability;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
|
|
||||||
import forge.card.ColorSet;
|
import forge.card.ColorSet;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.card.mana.ManaAtom;
|
import forge.card.mana.ManaAtom;
|
||||||
import forge.card.mana.ManaCostShard;
|
import forge.card.mana.ManaCostShard;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.GameActionUtil;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.card.CounterType;
|
|
||||||
import forge.game.mana.Mana;
|
import forge.game.mana.Mana;
|
||||||
import forge.game.mana.ManaPool;
|
import forge.game.mana.ManaPool;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
@@ -37,15 +37,11 @@ import forge.game.replacement.*;
|
|||||||
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.ZoneType;
|
|
||||||
import forge.util.Lang;
|
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -62,6 +58,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
private final String origProduced;
|
private final String origProduced;
|
||||||
private String lastExpressChoice = "";
|
private String lastExpressChoice = "";
|
||||||
private final String manaRestrictions;
|
private final String manaRestrictions;
|
||||||
|
private String extraManaRestrictions = "";
|
||||||
private final String cannotCounterSpell;
|
private final String cannotCounterSpell;
|
||||||
private final String addsKeywords;
|
private final String addsKeywords;
|
||||||
private final String addsKeywordsType;
|
private final String addsKeywordsType;
|
||||||
@@ -69,7 +66,6 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
private final String addsCounters;
|
private final String addsCounters;
|
||||||
private final String triggersWhenSpent;
|
private final String triggersWhenSpent;
|
||||||
private final boolean persistentMana;
|
private final boolean persistentMana;
|
||||||
private String manaReplaceType;
|
|
||||||
|
|
||||||
private transient List<Mana> lastManaProduced = Lists.newArrayList();
|
private transient List<Mana> lastManaProduced = Lists.newArrayList();
|
||||||
|
|
||||||
@@ -99,7 +95,6 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
this.addsCounters = params.get("AddsCounters");
|
this.addsCounters = params.get("AddsCounters");
|
||||||
this.triggersWhenSpent = params.get("TriggersWhenSpent");
|
this.triggersWhenSpent = params.get("TriggersWhenSpent");
|
||||||
this.persistentMana = (null != params.get("PersistentMana")) && "True".equalsIgnoreCase(params.get("PersistentMana"));
|
this.persistentMana = (null != params.get("PersistentMana")) && "True".equalsIgnoreCase(params.get("PersistentMana"));
|
||||||
this.manaReplaceType = params.containsKey("ManaReplaceType") ? params.get("ManaReplaceType") : "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,14 +121,26 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
public final void produceMana(final String produced, final Player player, SpellAbility sa) {
|
public final void produceMana(final String produced, final Player player, SpellAbility sa) {
|
||||||
final Card source = this.getSourceCard();
|
final Card source = this.getSourceCard();
|
||||||
final ManaPool manaPool = player.getManaPool();
|
final ManaPool manaPool = player.getManaPool();
|
||||||
String afterReplace = applyManaReplacement(sa, produced);
|
String afterReplace = produced;
|
||||||
|
|
||||||
|
SpellAbility root = sa == null ? null : sa.getRootAbility();
|
||||||
|
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(source);
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(source);
|
||||||
repParams.put(AbilityKey.Mana, produced);
|
repParams.put(AbilityKey.Mana, afterReplace);
|
||||||
repParams.put(AbilityKey.Player, player);
|
repParams.put(AbilityKey.Player, player);
|
||||||
repParams.put(AbilityKey.AbilityMana, sa);
|
repParams.put(AbilityKey.AbilityMana, root);
|
||||||
if (player.getGame().getReplacementHandler().run(ReplacementType.ProduceMana, repParams) != ReplacementResult.NotReplaced) {
|
repParams.put(AbilityKey.Activator, root == null ? null : root.getActivatingPlayer());
|
||||||
|
|
||||||
|
switch (player.getGame().getReplacementHandler().run(ReplacementType.ProduceMana, repParams)) {
|
||||||
|
case NotReplaced:
|
||||||
|
break;
|
||||||
|
case Updated:
|
||||||
|
afterReplace = (String) repParams.get(AbilityKey.Mana);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//clear lastProduced
|
//clear lastProduced
|
||||||
this.lastManaProduced.clear();
|
this.lastManaProduced.clear();
|
||||||
|
|
||||||
@@ -159,14 +166,14 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
// Run triggers
|
// Run triggers
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(source);
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(source);
|
||||||
runParams.put(AbilityKey.Player, player);
|
runParams.put(AbilityKey.Player, player);
|
||||||
runParams.put(AbilityKey.AbilityMana, sa);
|
|
||||||
runParams.put(AbilityKey.Produced, afterReplace);
|
runParams.put(AbilityKey.Produced, afterReplace);
|
||||||
|
runParams.put(AbilityKey.AbilityMana, root);
|
||||||
|
runParams.put(AbilityKey.Activator, root == null ? null : root.getActivatingPlayer());
|
||||||
|
|
||||||
player.getGame().getTriggerHandler().runTrigger(TriggerType.TapsForMana, runParams, false);
|
player.getGame().getTriggerHandler().runTrigger(TriggerType.TapsForMana, runParams, false);
|
||||||
if (source.isLand()) {
|
if (source.isLand() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost() ) {
|
||||||
player.setTappedLandForManaThisTurn(true);
|
player.setTappedLandForManaThisTurn(true);
|
||||||
}
|
}
|
||||||
// Clear Mana replacement
|
|
||||||
this.manaReplaceType = "";
|
|
||||||
} // end produceMana(String)
|
} // end produceMana(String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,61 +243,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
String[] parse = this.addsCounters.split("_");
|
String[] parse = this.addsCounters.split("_");
|
||||||
// Convert random SVars if there are other cards with this effect
|
// Convert random SVars if there are other cards with this effect
|
||||||
if (c.isValid(parse[0], c.getController(), c, null)) {
|
if (c.isValid(parse[0], c.getController(), c, null)) {
|
||||||
final Game game = this.sourceCard.getGame();
|
GameActionUtil.createETBCountersEffect(sourceCard, c, controller, parse[1], parse[2]);
|
||||||
final Card eff = new Card(game.nextCardId(), game);
|
|
||||||
eff.setTimestamp(game.getNextTimestamp());
|
|
||||||
eff.setName(sourceCard.getName() + "'s Effect");
|
|
||||||
eff.addType("Effect");
|
|
||||||
eff.setOwner(controller);
|
|
||||||
|
|
||||||
eff.setImageKey(sourceCard.getImageKey());
|
|
||||||
eff.setColor(MagicColor.COLORLESS);
|
|
||||||
eff.setImmutable(true);
|
|
||||||
// try to get the SpellAbility from the mana ability
|
|
||||||
//eff.setEffectSource((SpellAbility)null);
|
|
||||||
|
|
||||||
eff.addRemembered(c);
|
|
||||||
|
|
||||||
String abStr = "DB$ PutCounter | Defined$ ReplacedCard | CounterType$ " + parse[1]
|
|
||||||
+ " | ETB$ True | CounterNum$ " + parse[2];
|
|
||||||
|
|
||||||
SpellAbility sa = AbilityFactory.getAbility(abStr, c);
|
|
||||||
if (!StringUtils.isNumeric(parse[2])) {
|
|
||||||
sa.setSVar(parse[2], sourceCard.getSVar(parse[2]));
|
|
||||||
}
|
|
||||||
CardFactoryUtil.setupETBReplacementAbility(sa);
|
|
||||||
|
|
||||||
String desc = "It enters the battlefield with ";
|
|
||||||
desc += Lang.nounWithNumeral(parse[2], CounterType.getType(parse[1]).getName() + " counter");
|
|
||||||
desc += " on it.";
|
|
||||||
|
|
||||||
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;
|
|
||||||
|
|
||||||
ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true);
|
|
||||||
re.setLayer(ReplacementLayer.Other);
|
|
||||||
re.setOverridingAbility(sa);
|
|
||||||
|
|
||||||
eff.addReplacementEffect(re);
|
|
||||||
|
|
||||||
// Forgot Trigger
|
|
||||||
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Stack | Destination$ Any | TriggerZones$ Command | Static$ True";
|
|
||||||
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
|
|
||||||
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
|
|
||||||
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
|
|
||||||
|
|
||||||
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, eff);
|
|
||||||
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, eff);
|
|
||||||
saForget.setSubAbility(saExile);
|
|
||||||
|
|
||||||
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, eff, true);
|
|
||||||
parsedTrigger.setOverridingAbility(saForget);
|
|
||||||
eff.addTrigger(parsedTrigger);
|
|
||||||
eff.updateStateForView();
|
|
||||||
|
|
||||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
|
||||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
|
||||||
game.getAction().moveTo(ZoneType.Command, eff, null);
|
|
||||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +268,15 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
* @return a {@link java.lang.String} object.
|
* @return a {@link java.lang.String} object.
|
||||||
*/
|
*/
|
||||||
public String getManaRestrictions() {
|
public String getManaRestrictions() {
|
||||||
return this.manaRestrictions;
|
return manaRestrictions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtraManaRestriction(String str) {
|
||||||
|
this.extraManaRestrictions = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean meetsManaRestrictions(final SpellAbility sa) {
|
||||||
|
return meetsManaRestrictions(sa, this.manaRestrictions) && meetsManaRestrictions(sa, this.extraManaRestrictions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -327,14 +288,14 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public boolean meetsManaRestrictions(final SpellAbility sa) {
|
public boolean meetsManaRestrictions(final SpellAbility sa, String restrictions) {
|
||||||
// No restrictions
|
// No restrictions
|
||||||
if (this.manaRestrictions.isEmpty()) {
|
if (restrictions.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop over restrictions
|
// Loop over restrictions
|
||||||
for (String restriction : this.manaRestrictions.split(",")) {
|
for (String restriction : restrictions.split(",")) {
|
||||||
if (restriction.equals("nonSpell")) {
|
if (restriction.equals("nonSpell")) {
|
||||||
return !sa.isSpell();
|
return !sa.isSpell();
|
||||||
}
|
}
|
||||||
@@ -524,10 +485,6 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
return this.getOrigProduced().contains("Special");
|
return this.getOrigProduced().contains("Special");
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean canProduce(final String s) {
|
|
||||||
return canProduce(s, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* canProduce.
|
* canProduce.
|
||||||
@@ -552,24 +509,9 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
if (isComboMana()) {
|
if (isComboMana()) {
|
||||||
return getComboColors().contains(s);
|
return getComboColors().contains(s);
|
||||||
}
|
}
|
||||||
if (sa != null) {
|
|
||||||
return applyManaReplacement(sa, origProduced).contains(s);
|
|
||||||
}
|
|
||||||
return origProduced.contains(s);
|
return origProduced.contains(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* isBasic.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return a boolean.
|
|
||||||
*/
|
|
||||||
public final boolean isBasic() {
|
|
||||||
return this.getOrigProduced().length() == 1 || this.getOrigProduced().contains("Any")
|
|
||||||
|| this.getOrigProduced().contains("Chosen");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public final boolean equals(final Object o) {
|
public final boolean equals(final Object o) {
|
||||||
@@ -645,81 +587,59 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
return this.persistentMana;
|
return this.persistentMana;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
boolean abilityProducesManaColor(final SpellAbility am, final byte neededColor) {
|
||||||
* @return the manaReplaceType
|
if (0 != (neededColor & ManaAtom.GENERIC)) {
|
||||||
*/
|
return true;
|
||||||
public String getManaReplaceType() {
|
|
||||||
return manaReplaceType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (isAnyMana()) {
|
||||||
* setManaReplaceType.
|
return true;
|
||||||
*/
|
|
||||||
public void setManaReplaceType(final String type) {
|
|
||||||
this.manaReplaceType = type;
|
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* <p>
|
// check for produce mana replacement effects - they mess this up, so just use the mana ability
|
||||||
* applyManaReplacement.
|
final Card source = am.getHostCard();
|
||||||
* </p>
|
final Player activator = am.getActivatingPlayer();
|
||||||
* @return a String
|
final Game g = source.getGame();
|
||||||
*/
|
final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
|
||||||
public static String applyManaReplacement(final SpellAbility sa, final String original) {
|
repParams.put(AbilityKey.Mana, getOrigProduced());
|
||||||
final Map<String, String> repMap = Maps.newHashMap();
|
repParams.put(AbilityKey.Affected, source);
|
||||||
final Player act = sa != null ? sa.getActivatingPlayer() : null;
|
repParams.put(AbilityKey.Player, activator);
|
||||||
final String manaReplace = sa != null ? sa.getManaPart().getManaReplaceType(): "";
|
repParams.put(AbilityKey.AbilityMana, am.getRootAbility());
|
||||||
if (manaReplace.isEmpty()) {
|
|
||||||
if (act != null && act.getLandsPlayedThisTurn() > 0 && sa.hasParam("ReplaceIfLandPlayed")) {
|
for (final Player p : g.getPlayers()) {
|
||||||
return sa.getParam("ReplaceIfLandPlayed");
|
for (final Card crd : p.getAllCards()) {
|
||||||
}
|
for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) {
|
||||||
return original;
|
if (replacementEffect.requirementsCheck(g)
|
||||||
}
|
&& replacementEffect.getMode() == ReplacementType.ProduceMana
|
||||||
if (manaReplace.startsWith("Any")) {
|
&& replacementEffect.canReplace(repParams)
|
||||||
// Replace any type and amount
|
&& replacementEffect.zonesCheck(g.getZoneOf(crd))) {
|
||||||
String replaced = manaReplace.split("->")[1];
|
return true;
|
||||||
if (replaced.equals("Any")) {
|
|
||||||
byte rs = MagicColor.GREEN;
|
|
||||||
if (act != null) {
|
|
||||||
rs = act.getController().chooseColor("Choose a color", sa, ColorSet.ALL_COLORS);
|
|
||||||
}
|
|
||||||
replaced = MagicColor.toShortString(rs);
|
|
||||||
}
|
|
||||||
return replaced;
|
|
||||||
}
|
|
||||||
final Pattern splitter = Pattern.compile("->");
|
|
||||||
// Replace any type
|
|
||||||
for (String part : manaReplace.split(" & ")) {
|
|
||||||
final String[] v = splitter.split(part, 2);
|
|
||||||
// TODO Colorless mana replacement is probably different now?
|
|
||||||
if (v[0].equals("Colorless")) {
|
|
||||||
repMap.put("[0-9][0-9]?", v.length > 1 ? v[1].trim() : "");
|
|
||||||
} else {
|
|
||||||
repMap.put(v[0], v.length > 1 ? v[1].trim() : "");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Handle different replacement simultaneously
|
|
||||||
Pattern pattern = Pattern.compile(StringUtils.join(repMap.keySet().iterator(), "|"));
|
|
||||||
Matcher m = pattern.matcher(original);
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
while(m.find()) {
|
|
||||||
if (m.group().matches("[0-9][0-9]?")) {
|
|
||||||
final String rep = StringUtils.repeat(repMap.get("[0-9][0-9]?") + " ",
|
|
||||||
Integer.parseInt(m.group())).trim();
|
|
||||||
m.appendReplacement(sb, rep);
|
|
||||||
} else {
|
|
||||||
m.appendReplacement(sb, repMap.get(m.group()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.appendTail(sb);
|
|
||||||
String replaced = sb.toString();
|
if (am.getApi() == ApiType.ManaReflected) {
|
||||||
while (replaced.contains("Any")) {
|
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am);
|
||||||
byte rs = MagicColor.GREEN;
|
for (final String color : reflectableColors) {
|
||||||
if (act != null) {
|
if (0 != (neededColor & ManaAtom.fromName(color))) {
|
||||||
rs = act.getController().chooseColor("Choose a color", sa, ColorSet.ALL_COLORS);
|
return true;
|
||||||
}
|
}
|
||||||
replaced = replaced.replaceFirst("Any", MagicColor.toShortString(rs));
|
|
||||||
}
|
}
|
||||||
return replaced;
|
}
|
||||||
|
else {
|
||||||
|
// treat special mana if it always can be paid
|
||||||
|
if (isSpecialMana()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String colorsProduced = isComboMana() ? getComboColors() : mana();
|
||||||
|
for (final String color : colorsProduced.split(" ")) {
|
||||||
|
if (0 != (neededColor & ManaAtom.fromName(color))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end class AbilityMana
|
} // end class AbilityMana
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ package forge.game.spellability;
|
|||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.ability.effects.ChangeZoneAllEffect;
|
|
||||||
import forge.game.ability.effects.ChangeZoneEffect;
|
|
||||||
import forge.game.ability.effects.ManaEffect;
|
|
||||||
import forge.game.ability.effects.ManaReflectedEffect;
|
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
|
|
||||||
@@ -92,11 +88,11 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
|
|||||||
|
|
||||||
effect = api.getSpellEffect();
|
effect = api.getSpellEffect();
|
||||||
|
|
||||||
if (effect instanceof ManaEffect || effect instanceof ManaReflectedEffect) {
|
if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) {
|
||||||
this.setManaPart(new AbilityManaPart(ca, mapParams));
|
this.setManaPart(new AbilityManaPart(ca, mapParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effect instanceof ChangeZoneEffect || effect instanceof ChangeZoneAllEffect) {
|
if (api.equals(ApiType.ChangeZone) || api.equals(ApiType.ChangeZoneAll)) {
|
||||||
AbilityFactory.adjustChangeZoneTarget(mapParams, this);
|
AbilityFactory.adjustChangeZoneTarget(mapParams, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,6 +152,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
private TargetRestrictions targetRestrictions = null;
|
private TargetRestrictions targetRestrictions = null;
|
||||||
private TargetChoices targetChosen = new TargetChoices();
|
private TargetChoices targetChosen = new TargetChoices();
|
||||||
|
|
||||||
|
private Integer dividedValue = null;
|
||||||
|
|
||||||
private SpellAbilityView view;
|
private SpellAbilityView view;
|
||||||
|
|
||||||
private StaticAbility mayPlay = null;
|
private StaticAbility mayPlay = null;
|
||||||
@@ -159,6 +161,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
private CardCollection lastStateBattlefield = null;
|
private CardCollection lastStateBattlefield = null;
|
||||||
private CardCollection lastStateGraveyard = null;
|
private CardCollection lastStateGraveyard = null;
|
||||||
|
|
||||||
|
private CardCollection rollbackEffects = new CardCollection();
|
||||||
|
|
||||||
private CardDamageMap damageMap = null;
|
private CardDamageMap damageMap = null;
|
||||||
private CardDamageMap preventMap = null;
|
private CardDamageMap preventMap = null;
|
||||||
private CardZoneTable changeZoneTable = null;
|
private CardZoneTable changeZoneTable = null;
|
||||||
@@ -233,19 +237,101 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
view.updateDescription(this); //description can change if host card does
|
view.updateDescription(this); //description can change if host card does
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canThisProduce(final String s) {
|
||||||
|
AbilityManaPart mp = getManaPart();
|
||||||
|
if (mp != null && metConditions() && mp.canProduce(s, this)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canProduce(final String s) {
|
||||||
|
if (canThisProduce(s)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.subAbility != null ? this.subAbility.canProduce(s) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isManaAbilityFor(SpellAbility saPaidFor, byte colorNeeded) {
|
||||||
|
// is root ability
|
||||||
|
if (this.getParent() == null) {
|
||||||
|
if (!canPlay()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isAbility() && getRestrictions().isInstantSpeed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AbilityManaPart mp = getManaPart();
|
||||||
|
if (mp != null && metConditions() && mp.meetsManaRestrictions(saPaidFor) && mp.abilityProducesManaColor(this, colorNeeded)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return this.subAbility != null ? this.subAbility.isManaAbilityFor(saPaidFor, colorNeeded) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isManaCannotCounter(SpellAbility saPaidFor) {
|
||||||
|
AbilityManaPart mp = getManaPart();
|
||||||
|
if (mp != null && metConditions() && mp.meetsManaRestrictions(saPaidFor) && mp.cannotCounterPaidWith(saPaidFor)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return this.subAbility != null ? this.subAbility.isManaCannotCounter(saPaidFor) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int amountOfManaGenerated(boolean multiply) {
|
||||||
|
int result = 0;
|
||||||
|
AbilityManaPart mp = getManaPart();
|
||||||
|
if (mp != null && metConditions()) {
|
||||||
|
int amount = hasParam("Amount") ? AbilityUtils.calculateAmount(getHostCard(), getParam("Amount"), this) : 1;
|
||||||
|
|
||||||
|
if (!multiply || mp.isAnyMana() || mp.isComboMana() || mp.isSpecialMana()) {
|
||||||
|
result += amount;
|
||||||
|
} else {
|
||||||
|
// For cards that produce like {C}{R} vs cards that produce {R}{R}.
|
||||||
|
result += mp.mana().split(" ").length * amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int totalAmountOfManaGenerated(SpellAbility saPaidFor, boolean multiply) {
|
||||||
|
int result = 0;
|
||||||
|
AbilityManaPart mp = getManaPart();
|
||||||
|
if (mp != null && metConditions() && mp.meetsManaRestrictions(saPaidFor)) {
|
||||||
|
result += amountOfManaGenerated(multiply);
|
||||||
|
}
|
||||||
|
result += subAbility != null ? subAbility.totalAmountOfManaGenerated(saPaidFor, multiply) : 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setManaExpressChoice(ColorSet cs) {
|
||||||
|
AbilityManaPart mp = getManaPart();
|
||||||
|
if (mp != null) {
|
||||||
|
mp.setExpressChoice(cs);
|
||||||
|
}
|
||||||
|
if (subAbility != null) {
|
||||||
|
subAbility.setManaExpressChoice(cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final AbilityManaPart getManaPart() {
|
public final AbilityManaPart getManaPart() {
|
||||||
return manaPart;
|
return manaPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final AbilityManaPart getManaPartRecursive() {
|
public final List<AbilityManaPart> getAllManaParts() {
|
||||||
SpellAbility tail = this;
|
AbilityManaPart mp = getManaPart();
|
||||||
while (tail != null) {
|
if (mp == null && subAbility == null) {
|
||||||
if (tail.manaPart != null) {
|
return ImmutableList.of();
|
||||||
return tail.manaPart;
|
|
||||||
}
|
}
|
||||||
tail = tail.getSubAbility();
|
List<AbilityManaPart> result = Lists.newArrayList();
|
||||||
|
if (mp != null) {
|
||||||
|
result.add(mp);
|
||||||
}
|
}
|
||||||
return null;
|
if (subAbility != null) {
|
||||||
|
result.addAll(subAbility.getAllManaParts());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isManaAbility() {
|
public final boolean isManaAbility() {
|
||||||
@@ -262,7 +348,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getManaPartRecursive() != null;
|
SpellAbility tail = this;
|
||||||
|
while (tail != null) {
|
||||||
|
if (tail.manaPart != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
tail = tail.getSubAbility();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void setManaPart(AbilityManaPart manaPart0) {
|
protected final void setManaPart(AbilityManaPart manaPart0) {
|
||||||
@@ -460,6 +553,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
conditions = condition;
|
conditions = condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean metConditions() {
|
||||||
|
return getConditions() != null && getConditions().areMet(this);
|
||||||
|
}
|
||||||
|
|
||||||
public List<Mana> getPayingMana() {
|
public List<Mana> getPayingMana() {
|
||||||
return payingMana;
|
return payingMana;
|
||||||
}
|
}
|
||||||
@@ -1101,7 +1198,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
|
|
||||||
if (hasParam("TargetingPlayerControls") && entity instanceof Card) {
|
if (hasParam("TargetingPlayerControls") && entity instanceof Card) {
|
||||||
final Card c = (Card) entity;
|
final Card c = (Card) entity;
|
||||||
if (!c.getController().equals(targetingPlayer)) {
|
if (!c.getController().equals(getTargetingPlayer())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1421,6 +1518,35 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
targetChosen = new TargetChoices();
|
targetChosen = new TargetChoices();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a boolean dividedAsYouChoose
|
||||||
|
*/
|
||||||
|
public boolean isDividedAsYouChoose() {
|
||||||
|
return hasParam("DividedAsYouChoose");
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void addDividedAllocation(final GameObject tgt, final Integer portionAllocated) {
|
||||||
|
getTargets().addDividedAllocation(tgt, portionAllocated);
|
||||||
|
}
|
||||||
|
public Integer getDividedValue(GameObject c) {
|
||||||
|
return getTargets().getDividedValue(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalDividedValue() {
|
||||||
|
return getTargets().getTotalDividedValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getDividedValue() {
|
||||||
|
return this.dividedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStillToDivide() {
|
||||||
|
if (!isDividedAsYouChoose() || dividedValue == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return dividedValue - getTotalDividedValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the first target.
|
* Reset the first target.
|
||||||
*
|
*
|
||||||
@@ -1428,16 +1554,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
public void resetFirstTarget(GameObject c, SpellAbility originalSA) {
|
public void resetFirstTarget(GameObject c, SpellAbility originalSA) {
|
||||||
SpellAbility sa = this;
|
SpellAbility sa = this;
|
||||||
while (sa != null) {
|
while (sa != null) {
|
||||||
if (sa.targetRestrictions != null) {
|
if (sa.usesTargeting()) {
|
||||||
sa.targetChosen = new TargetChoices();
|
sa.resetTargets();
|
||||||
sa.targetChosen.add(c);
|
sa.getTargets().add(c);
|
||||||
if (!originalSA.targetRestrictions.getDividedMap().isEmpty()) {
|
if (!originalSA.getTargets().getDividedValues().isEmpty()) {
|
||||||
sa.targetRestrictions.addDividedAllocation(c,
|
sa.addDividedAllocation(c, Iterables.getFirst(originalSA.getTargets().getDividedValues(), null));
|
||||||
Iterables.getFirst(originalSA.targetRestrictions.getDividedMap().values(), null));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sa = sa.subAbility;
|
sa = sa.getSubAbility();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1454,7 +1579,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isZeroTargets() {
|
public boolean isZeroTargets() {
|
||||||
return getTargetRestrictions().getMinTargets(hostCard, this) == 0 && getTargets().size() == 0;
|
return getMinTargets() == 0 && getTargets().size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMinTargetChosen() {
|
public boolean isMinTargetChosen() {
|
||||||
@@ -1728,6 +1853,27 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return p != null && p.isTargeting(o);
|
return p != null && p.isTargeting(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean setupNewTargets(Player forceTargetingPlayer) {
|
||||||
|
// Skip to paying if parent ability doesn't target and has no subAbilities.
|
||||||
|
// (or trigger case where its already targeted)
|
||||||
|
SpellAbility currentAbility = this;
|
||||||
|
do {
|
||||||
|
if (currentAbility.usesTargeting()) {
|
||||||
|
TargetChoices oldTargets = currentAbility.getTargets();
|
||||||
|
if (forceTargetingPlayer.getController().chooseNewTargetsFor(currentAbility, null, true) == null) {
|
||||||
|
currentAbility.setTargets(oldTargets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final AbilitySub subAbility = currentAbility.getSubAbility();
|
||||||
|
if (subAbility != null) {
|
||||||
|
// This is necessary for "TargetsWithDefinedController$ ParentTarget"
|
||||||
|
subAbility.setParent(currentAbility);
|
||||||
|
}
|
||||||
|
currentAbility = subAbility;
|
||||||
|
} while (currentAbility != null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean setupTargets() {
|
public boolean setupTargets() {
|
||||||
// Skip to paying if parent ability doesn't target and has no subAbilities.
|
// Skip to paying if parent ability doesn't target and has no subAbilities.
|
||||||
// (or trigger case where its already targeted)
|
// (or trigger case where its already targeted)
|
||||||
@@ -1745,6 +1891,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
} else {
|
} else {
|
||||||
targetingPlayer = getActivatingPlayer();
|
targetingPlayer = getActivatingPlayer();
|
||||||
}
|
}
|
||||||
|
// don't set targeting player when forceful target,
|
||||||
|
// "targeting player controls" should not be reset when the spell is copied
|
||||||
currentAbility.setTargetingPlayer(targetingPlayer);
|
currentAbility.setTargetingPlayer(targetingPlayer);
|
||||||
if (!targetingPlayer.getController().chooseTargetsFor(currentAbility)) {
|
if (!targetingPlayer.getController().chooseTargetsFor(currentAbility)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1760,11 +1908,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public final void clearTargets() {
|
public final void clearTargets() {
|
||||||
final TargetRestrictions tg = getTargetRestrictions();
|
if (usesTargeting()) {
|
||||||
if (tg != null) {
|
|
||||||
resetTargets();
|
resetTargets();
|
||||||
if (hasParam("DividedAsYouChoose")) {
|
if (isDividedAsYouChoose()) {
|
||||||
tg.calculateStillToDivide(getParam("DividedAsYouChoose"), getHostCard(), this);
|
this.dividedValue = AbilityUtils.calculateAmount(getHostCard(), this.getParam("DividedAsYouChoose"), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1830,9 +1977,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
public boolean tracksManaSpent() {
|
public boolean tracksManaSpent() {
|
||||||
if (hostCard == null || hostCard.getRules() == null) { return false; }
|
if (hostCard == null || hostCard.getRules() == null) { return false; }
|
||||||
|
|
||||||
if (hostCard.hasKeyword(Keyword.SUNBURST)) {
|
if (isSpell() && hostCard.hasConverge()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String text = hostCard.getRules().getOracleText();
|
String text = hostCard.getRules().getOracleText();
|
||||||
if (isSpell() && text.contains("was spent to cast")) {
|
if (isSpell() && text.contains("was spent to cast")) {
|
||||||
return true;
|
return true;
|
||||||
@@ -1969,7 +2117,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
String mana = manaPart.mana();
|
String mana = manaPart.mana();
|
||||||
if (!mana.equals("Any")) {
|
if (!mana.equals("Any")) {
|
||||||
score += mana.length();
|
score += mana.length();
|
||||||
if (!manaPart.canProduce("C")) {
|
if (!canProduce("C")) {
|
||||||
// Producing colorless should produce a slightly lower score
|
// Producing colorless should produce a slightly lower score
|
||||||
score += 1;
|
score += 1;
|
||||||
}
|
}
|
||||||
@@ -2134,4 +2282,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
public boolean checkRestrictions(Card host, Player activator) {
|
public boolean checkRestrictions(Card host, Player activator) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addRollbackEffect(Card eff) {
|
||||||
|
rollbackEffects.add(eff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rollback() {
|
||||||
|
for (Card c : rollbackEffects) {
|
||||||
|
c.ceaseToExist();
|
||||||
|
}
|
||||||
|
rollbackEffects.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,10 @@ import forge.util.TextUtil;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@@ -85,7 +84,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
|
|||||||
private Integer xManaPaid = null;
|
private Integer xManaPaid = null;
|
||||||
|
|
||||||
// Other Paid things
|
// Other Paid things
|
||||||
private final HashMap<String, CardCollection> paidHash;
|
private final Map<String, CardCollection> paidHash;
|
||||||
|
|
||||||
// Additional info
|
// Additional info
|
||||||
// is Kicked, is Buyback
|
// is Kicked, is Buyback
|
||||||
@@ -96,7 +95,6 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
|
|||||||
|
|
||||||
private final Map<String, String> storedSVars = Maps.newHashMap();
|
private final Map<String, String> storedSVars = Maps.newHashMap();
|
||||||
|
|
||||||
private final List<ZoneType> zonesToOpen;
|
|
||||||
private final Map<Player, Object> playersWithValidTargets;
|
private final Map<Player, Object> playersWithValidTargets;
|
||||||
|
|
||||||
private final StackItemView view;
|
private final StackItemView view;
|
||||||
@@ -109,7 +107,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
|
|||||||
activatingPlayer = sa.getActivatingPlayer();
|
activatingPlayer = sa.getActivatingPlayer();
|
||||||
|
|
||||||
// Payment info
|
// Payment info
|
||||||
paidHash = new HashMap<>(ability.getPaidHash());
|
paidHash = Maps.newHashMap(ability.getPaidHash());
|
||||||
ability.resetPaidHash();
|
ability.resetPaidHash();
|
||||||
splicedCards = sa.getSplicedCards();
|
splicedCards = sa.getSplicedCards();
|
||||||
|
|
||||||
@@ -149,18 +147,13 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
|
|||||||
|
|
||||||
//store zones to open and players to open them for at the time the SpellAbility first goes on the stack based on the selected targets
|
//store zones to open and players to open them for at the time the SpellAbility first goes on the stack based on the selected targets
|
||||||
if (tc == null) {
|
if (tc == null) {
|
||||||
zonesToOpen = null;
|
|
||||||
playersWithValidTargets = null;
|
playersWithValidTargets = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
zonesToOpen = new ArrayList<>();
|
playersWithValidTargets = Maps.newHashMap();
|
||||||
playersWithValidTargets = new HashMap<>();
|
|
||||||
for (Card card : tc.getTargetCards()) {
|
for (Card card : tc.getTargetCards()) {
|
||||||
ZoneType zoneType = card.getZone() != null ? card.getZone().getZoneType() : null;
|
ZoneType zoneType = card.getZone() != null ? card.getZone().getZoneType() : null;
|
||||||
if (zoneType != ZoneType.Battlefield) { //don't need to worry about targets on battlefield
|
if (zoneType != ZoneType.Battlefield) { //don't need to worry about targets on battlefield
|
||||||
if (zoneType != null && !zonesToOpen.contains(zoneType)) {
|
|
||||||
zonesToOpen.add(zoneType);
|
|
||||||
}
|
|
||||||
playersWithValidTargets.put(card.getController(), null);
|
playersWithValidTargets.put(card.getController(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,20 +246,13 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
|
|||||||
return tc;
|
return tc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final List<ZoneType> getZonesToOpen() {
|
|
||||||
return zonesToOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Map<Player, Object> getPlayersWithValidTargets() {
|
public final Map<Player, Object> getPlayersWithValidTargets() {
|
||||||
return playersWithValidTargets;
|
return playersWithValidTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateTarget(TargetChoices target) {
|
public void updateTarget(TargetChoices target) {
|
||||||
updateTarget(target, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateTarget(TargetChoices target, GameObject oldTarget, GameObject newTarget) {
|
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
|
TargetChoices oldTarget = tc;
|
||||||
tc = target;
|
tc = target;
|
||||||
ability.setTargets(tc);
|
ability.setTargets(tc);
|
||||||
stackDescription = ability.getStackDescription();
|
stackDescription = ability.getStackDescription();
|
||||||
@@ -274,54 +260,18 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
|
|||||||
view.updateTargetPlayers(this);
|
view.updateTargetPlayers(this);
|
||||||
view.updateText(this);
|
view.updateText(this);
|
||||||
|
|
||||||
if (ability.hasParam("DividedAsYouChoose")) {
|
|
||||||
// try to update DividedAsYouChoose after retargeting
|
|
||||||
Object toRemove = null;
|
|
||||||
Object toAdd = null;
|
|
||||||
HashMap<Object,Integer> map = ability.getTargetRestrictions().getDividedMap();
|
|
||||||
|
|
||||||
if (oldTarget != null) {
|
|
||||||
toRemove = oldTarget;
|
|
||||||
} else {
|
|
||||||
// try to deduce which target has been replaced
|
|
||||||
// (this may be imprecise, updateTarget should specify old target if possible)
|
|
||||||
for (Object obj : map.keySet()) {
|
|
||||||
if (!target.contains(obj)) {
|
|
||||||
toRemove = obj;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newTarget != null) {
|
|
||||||
toAdd = newTarget;
|
|
||||||
} else {
|
|
||||||
// try to deduce which target was added
|
|
||||||
// (this may be imprecise, updateTarget should specify new target if possible)
|
|
||||||
for (Object newTgts : target) {
|
|
||||||
if (!map.containsKey(newTgts)) {
|
|
||||||
toAdd = newTgts;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toRemove != null && toAdd != null) {
|
|
||||||
int div = map.get(toRemove);
|
|
||||||
map.remove(toRemove);
|
|
||||||
ability.getTargetRestrictions().addDividedAllocation(toAdd, div);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run BecomesTargetTrigger
|
// Run BecomesTargetTrigger
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
runParams.put(AbilityKey.SourceSA, ability);
|
runParams.put(AbilityKey.SourceSA, ability);
|
||||||
Set<Object> distinctObjects = new HashSet<>();
|
Set<GameObject> distinctObjects = Sets.newHashSet();
|
||||||
for (final Object tgt : target) {
|
for (final GameObject tgt : target) {
|
||||||
if (distinctObjects.contains(tgt)) {
|
if (oldTarget != null && oldTarget.contains(tgt)) {
|
||||||
|
// it was an old target, so don't trigger becomes target
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!distinctObjects.add(tgt)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
distinctObjects.add(tgt);
|
|
||||||
|
|
||||||
if (tgt instanceof Card && !((Card) tgt).hasBecomeTargetThisTurn()) {
|
if (tgt instanceof Card && !((Card) tgt).hasBecomeTargetThisTurn()) {
|
||||||
runParams.put(AbilityKey.FirstTime, null);
|
runParams.put(AbilityKey.FirstTime, null);
|
||||||
@@ -330,7 +280,8 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
|
|||||||
runParams.put(AbilityKey.Target, tgt);
|
runParams.put(AbilityKey.Target, tgt);
|
||||||
getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false);
|
getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTarget, runParams, false);
|
||||||
}
|
}
|
||||||
runParams.put(AbilityKey.Targets, target);
|
runParams = AbilityKey.newMap();
|
||||||
|
runParams.put(AbilityKey.Targets, distinctObjects);
|
||||||
getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTargetOnce, runParams, false);
|
getSourceCard().getGame().getTriggerHandler().runTrigger(TriggerType.BecomesTargetOnce, runParams, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.google.common.base.Predicates;
|
|||||||
import com.google.common.collect.ForwardingList;
|
import com.google.common.collect.ForwardingList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
@@ -30,7 +31,9 @@ import forge.game.card.CardCollectionView;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.util.collect.FCollection;
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -44,6 +47,8 @@ public class TargetChoices extends ForwardingList<GameObject> implements Cloneab
|
|||||||
|
|
||||||
private final FCollection<GameObject> targets = new FCollection<GameObject>();
|
private final FCollection<GameObject> targets = new FCollection<GameObject>();
|
||||||
|
|
||||||
|
private final Map<GameObject, Integer> dividedMap = Maps.newHashMap();
|
||||||
|
|
||||||
public final int getTotalTargetedCMC() {
|
public final int getTotalTargetedCMC() {
|
||||||
int totalCMC = 0;
|
int totalCMC = 0;
|
||||||
for (Card c : Iterables.filter(targets, Card.class)) {
|
for (Card c : Iterables.filter(targets, Card.class)) {
|
||||||
@@ -52,6 +57,7 @@ public class TargetChoices extends ForwardingList<GameObject> implements Cloneab
|
|||||||
return totalCMC;
|
return totalCMC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public final boolean add(final GameObject o) {
|
public final boolean add(final GameObject o) {
|
||||||
if (o instanceof Player || o instanceof Card || o instanceof SpellAbility) {
|
if (o instanceof Player || o instanceof Card || o instanceof SpellAbility) {
|
||||||
return super.add(o);
|
return super.add(o);
|
||||||
@@ -59,6 +65,22 @@ public class TargetChoices extends ForwardingList<GameObject> implements Cloneab
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> collection) {
|
||||||
|
boolean result = super.removeAll(collection);
|
||||||
|
for (Object e : collection) {
|
||||||
|
this.dividedMap.remove(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object object) {
|
||||||
|
boolean result = super.remove(object);
|
||||||
|
dividedMap.remove(object);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public final CardCollectionView getTargetCards() {
|
public final CardCollectionView getTargetCards() {
|
||||||
return new CardCollection(Iterables.filter(targets, Card.class));
|
return new CardCollection(Iterables.filter(targets, Card.class));
|
||||||
}
|
}
|
||||||
@@ -108,10 +130,31 @@ public class TargetChoices extends ForwardingList<GameObject> implements Cloneab
|
|||||||
public TargetChoices clone() {
|
public TargetChoices clone() {
|
||||||
TargetChoices tc = new TargetChoices();
|
TargetChoices tc = new TargetChoices();
|
||||||
tc.targets.addAll(targets);
|
tc.targets.addAll(targets);
|
||||||
|
tc.dividedMap.putAll(dividedMap);
|
||||||
return tc;
|
return tc;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected List<GameObject> delegate() {
|
protected List<GameObject> delegate() {
|
||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void addDividedAllocation(final GameObject tgt, final Integer portionAllocated) {
|
||||||
|
this.dividedMap.put(tgt, portionAllocated);
|
||||||
|
}
|
||||||
|
public Integer getDividedValue(GameObject c) {
|
||||||
|
return dividedMap.get(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Integer> getDividedValues() {
|
||||||
|
return dividedMap.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalDividedValue() {
|
||||||
|
int result = 0;
|
||||||
|
for (Integer i : getDividedValues()) {
|
||||||
|
if (i != null)
|
||||||
|
result += i;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,9 @@
|
|||||||
package forge.game.spellability;
|
package forge.game.spellability;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
@@ -76,11 +74,6 @@ public class TargetRestrictions {
|
|||||||
// What's the max total CMC of targets?
|
// What's the max total CMC of targets?
|
||||||
private String maxTotalCMC;
|
private String maxTotalCMC;
|
||||||
|
|
||||||
// For "Divided" cards. Is this better in TargetChoices?
|
|
||||||
private boolean dividedAsYouChoose = false;
|
|
||||||
private HashMap<Object, Integer> dividedMap = new HashMap<>();
|
|
||||||
private int stillToDivide = 0;
|
|
||||||
|
|
||||||
// Not sure what's up with Mandatory? Why wouldn't targeting be mandatory?
|
// Not sure what's up with Mandatory? Why wouldn't targeting be mandatory?
|
||||||
private boolean bMandatory = false;
|
private boolean bMandatory = false;
|
||||||
|
|
||||||
@@ -101,7 +94,6 @@ public class TargetRestrictions {
|
|||||||
this.maxTotalCMC = target.getMaxTotalCMC();
|
this.maxTotalCMC = target.getMaxTotalCMC();
|
||||||
this.tgtZone = target.getZone();
|
this.tgtZone = target.getZone();
|
||||||
this.saValidTargeting = target.getSAValidTargeting();
|
this.saValidTargeting = target.getSAValidTargeting();
|
||||||
this.dividedAsYouChoose = target.isDividedAsYouChoose();
|
|
||||||
this.uniqueTargets = target.isUniqueTargets();
|
this.uniqueTargets = target.isUniqueTargets();
|
||||||
this.singleZone = target.isSingleZone();
|
this.singleZone = target.isSingleZone();
|
||||||
this.differentControllers = target.isDifferentControllers();
|
this.differentControllers = target.isDifferentControllers();
|
||||||
@@ -728,82 +720,9 @@ public class TargetRestrictions {
|
|||||||
this.singleTarget = singleTarget;
|
this.singleTarget = singleTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a boolean dividedAsYouChoose
|
|
||||||
*/
|
|
||||||
public boolean isDividedAsYouChoose() {
|
|
||||||
return this.dividedAsYouChoose;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param divided the boolean to set
|
|
||||||
*/
|
|
||||||
public void setDividedAsYouChoose(boolean divided) {
|
|
||||||
this.dividedAsYouChoose = divided;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the amount remaining to distribute.
|
|
||||||
* @return int stillToDivide
|
|
||||||
*/
|
|
||||||
public int getStillToDivide() {
|
|
||||||
return this.stillToDivide;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param remaining set the amount still to be divided
|
|
||||||
*/
|
|
||||||
public void setStillToDivide(final int remaining) {
|
|
||||||
this.stillToDivide = remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void calculateStillToDivide(String toDistribute, Card source, SpellAbility sa) {
|
|
||||||
// Recalculate this value just in case it's variable
|
|
||||||
if (!this.dividedAsYouChoose) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isNumeric(toDistribute)) {
|
|
||||||
this.setStillToDivide(Integer.parseInt(toDistribute));
|
|
||||||
} else if ( source == null ) {
|
|
||||||
return; // such calls come from AbilityFactory.readTarget - at this moment we don't yet know X or any other variables
|
|
||||||
} else if (source.getSVar(toDistribute).equals("xPaid")) {
|
|
||||||
this.setStillToDivide(source.getXManaCostPaid());
|
|
||||||
} else {
|
|
||||||
this.setStillToDivide(AbilityUtils.calculateAmount(source, toDistribute, sa));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store divided amount relative to a specific card/player.
|
|
||||||
* @param tgt the targeted object
|
|
||||||
* @param portionAllocated the divided portion allocated
|
|
||||||
*/
|
|
||||||
public final void addDividedAllocation(final Object tgt, final Integer portionAllocated) {
|
|
||||||
this.dividedMap.remove(tgt);
|
|
||||||
this.dividedMap.put(tgt, portionAllocated);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the divided amount relative to a specific card/player.
|
|
||||||
* @param tgt the targeted object
|
|
||||||
* @return an int.
|
|
||||||
*/
|
|
||||||
public int getDividedValue(Object tgt) {
|
|
||||||
return this.dividedMap.get(tgt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<Object, Integer> getDividedMap() {
|
|
||||||
return this.dividedMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void applyTargetTextChanges(final SpellAbility sa) {
|
public final void applyTargetTextChanges(final SpellAbility sa) {
|
||||||
for (int i = 0; i < validTgts.length; i++) {
|
for (int i = 0; i < validTgts.length; i++) {
|
||||||
validTgts[i] = AbilityUtils.applyAbilityTextChangeEffects(originalValidTgts[i], sa);
|
validTgts[i] = AbilityUtils.applyAbilityTextChangeEffects(originalValidTgts[i], sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void changeValidTargets(final String[] validTgts) {
|
|
||||||
this.originalValidTgts = validTgts;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -781,8 +781,7 @@ public final class StaticAbilityContinuous {
|
|||||||
// add Replacement effects
|
// add Replacement effects
|
||||||
if (addReplacements != null) {
|
if (addReplacements != null) {
|
||||||
for (String rep : addReplacements) {
|
for (String rep : addReplacements) {
|
||||||
final ReplacementEffect actualRep = ReplacementHandler.parseReplacement(rep, affectedCard, false);
|
final ReplacementEffect actualRep = ReplacementHandler.parseReplacement(rep, affectedCard, false, stAb);
|
||||||
actualRep.setIntrinsic(false);
|
|
||||||
addedReplacementEffects.add(actualRep);
|
addedReplacementEffects.add(actualRep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -790,17 +789,12 @@ public final class StaticAbilityContinuous {
|
|||||||
// add triggers
|
// add triggers
|
||||||
if (addTriggers != null) {
|
if (addTriggers != null) {
|
||||||
for (final String trigger : addTriggers) {
|
for (final String trigger : addTriggers) {
|
||||||
final Trigger actualTrigger = TriggerHandler.parseTrigger(trigger, affectedCard, false);
|
final Trigger actualTrigger = TriggerHandler.parseTrigger(trigger, affectedCard, false, stAb);
|
||||||
// if the trigger has Execute param, which most trigger gained by Static Abilties should have
|
// if the trigger has Execute param, which most trigger gained by Static Abilties should have
|
||||||
// turn them into SpellAbility object before adding to card
|
// turn them into SpellAbility object before adding to card
|
||||||
// with that the TargetedCard does not need the Svars added to them anymore
|
// with that the TargetedCard does not need the Svars added to them anymore
|
||||||
// but only do it if the trigger doesn't already have a overriding ability
|
// but only do it if the trigger doesn't already have a overriding ability
|
||||||
if (actualTrigger.hasParam("Execute") && actualTrigger.getOverridingAbility() == null) {
|
|
||||||
// set overriding ability to the trigger
|
|
||||||
actualTrigger.setOverridingAbility(AbilityFactory.getAbility(affectedCard, actualTrigger.getParam("Execute"), stAb));
|
|
||||||
}
|
|
||||||
actualTrigger.setOriginalHost(hostCard);
|
actualTrigger.setOriginalHost(hostCard);
|
||||||
actualTrigger.setIntrinsic(false);
|
|
||||||
addedTrigger.add(actualTrigger);
|
addedTrigger.add(actualTrigger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package forge.game.trigger;
|
|||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
|
import forge.game.IHasSVars;
|
||||||
import forge.game.TriggerReplacementBase;
|
import forge.game.TriggerReplacementBase;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
@@ -562,12 +563,16 @@ public abstract class Trigger extends TriggerReplacementBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpellAbility ensureAbility() {
|
public SpellAbility ensureAbility(final IHasSVars sVarHolder) {
|
||||||
SpellAbility sa = getOverridingAbility();
|
SpellAbility sa = getOverridingAbility();
|
||||||
if (sa == null && hasParam("Execute")) {
|
if (sa == null && hasParam("Execute")) {
|
||||||
sa = AbilityFactory.getAbility(getHostCard(), getParam("Execute"));
|
sa = AbilityFactory.getAbility(getHostCard(), getParam("Execute"), sVarHolder);
|
||||||
setOverridingAbility(sa);
|
setOverridingAbility(sa);
|
||||||
}
|
}
|
||||||
return sa;
|
return sa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SpellAbility ensureAbility() {
|
||||||
|
return ensureAbility(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import forge.game.card.Card;
|
|||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,9 +63,8 @@ public class TriggerBecomesTargetOnce extends Trigger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasParam("ValidTarget")) {
|
if (hasParam("ValidTarget")) {
|
||||||
List<GameObject> targets = (List<GameObject>) runParams.get(AbilityKey.Targets);
|
|
||||||
boolean valid = false;
|
boolean valid = false;
|
||||||
for (GameObject b : targets) {
|
for (GameObject b : (Iterable<GameObject>) runParams.get(AbilityKey.Targets)) {
|
||||||
if (matchesValid(b, getParam("ValidTarget").split(","), this.getHostCard())) {
|
if (matchesValid(b, getParam("ValidTarget").split(","), this.getHostCard())) {
|
||||||
valid = true;
|
valid = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package forge.game.trigger;
|
|||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GlobalRuleChange;
|
import forge.game.GlobalRuleChange;
|
||||||
|
import forge.game.IHasSVars;
|
||||||
import forge.game.ability.AbilityFactory;
|
import forge.game.ability.AbilityFactory;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
@@ -124,9 +125,13 @@ public class TriggerHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Trigger parseTrigger(final String trigParse, final Card host, final boolean intrinsic) {
|
public static Trigger parseTrigger(final String trigParse, final Card host, final boolean intrinsic) {
|
||||||
|
return parseTrigger(trigParse, host, intrinsic, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Trigger parseTrigger(final String trigParse, final Card host, final boolean intrinsic, final IHasSVars sVarHolder) {
|
||||||
try {
|
try {
|
||||||
final Map<String, String> mapParams = TriggerHandler.parseParams(trigParse);
|
final Map<String, String> mapParams = TriggerHandler.parseParams(trigParse);
|
||||||
return TriggerHandler.parseTrigger(mapParams, host, intrinsic);
|
return TriggerHandler.parseTrigger(mapParams, host, intrinsic, sVarHolder);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String msg = "TriggerHandler:parseTrigger failed to parse";
|
String msg = "TriggerHandler:parseTrigger failed to parse";
|
||||||
Sentry.getContext().recordBreadcrumb(
|
Sentry.getContext().recordBreadcrumb(
|
||||||
@@ -138,12 +143,15 @@ public class TriggerHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Trigger parseTrigger(final Map<String, String> mapParams, final Card host, final boolean intrinsic) {
|
public static Trigger parseTrigger(final Map<String, String> mapParams, final Card host, final boolean intrinsic, final IHasSVars sVarHolder) {
|
||||||
Trigger ret = null;
|
Trigger ret = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final TriggerType type = TriggerType.smartValueOf(mapParams.get("Mode"));
|
final TriggerType type = TriggerType.smartValueOf(mapParams.get("Mode"));
|
||||||
ret = type.createTrigger(mapParams, host, intrinsic);
|
ret = type.createTrigger(mapParams, host, intrinsic);
|
||||||
|
if (sVarHolder != null) {
|
||||||
|
ret.ensureAbility(sVarHolder);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String msg = "TriggerHandler:parseTrigger failed to parse";
|
String msg = "TriggerHandler:parseTrigger failed to parse";
|
||||||
Sentry.getContext().recordBreadcrumb(
|
Sentry.getContext().recordBreadcrumb(
|
||||||
@@ -170,13 +178,7 @@ public class TriggerHandler {
|
|||||||
if (wt.getTriggers() != null)
|
if (wt.getTriggers() != null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
List<Trigger> trigger = Lists.newArrayList();
|
wt.setTriggers(getActiveTrigger(wt.getMode(), wt.getParams()));
|
||||||
for (final Trigger t : activeTriggers) {
|
|
||||||
if (canRunTrigger(t,wt.getMode(),wt.getParams())) {
|
|
||||||
trigger.add(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wt.setTriggers(trigger);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,4 +710,14 @@ public class TriggerHandler {
|
|||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Trigger> getActiveTrigger(final TriggerType mode, final Map<AbilityKey, Object> runParams) {
|
||||||
|
List<Trigger> trigger = Lists.newArrayList();
|
||||||
|
for (final Trigger t : activeTriggers) {
|
||||||
|
if (canRunTrigger(t, mode, runParams)) {
|
||||||
|
trigger.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ package forge.game.trigger;
|
|||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.player.Player;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
|
|
||||||
@@ -68,25 +67,19 @@ public class TriggerTapsForMana extends Trigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasParam("ValidCard")) {
|
if (hasParam("ValidCard")) {
|
||||||
final Card tapper = (Card) runParams.get(AbilityKey.Card);
|
if (!matchesValid(runParams.get(AbilityKey.Card), getParam("ValidCard").split(","), getHostCard())) {
|
||||||
if (!tapper.isValid(getParam("ValidCard").split(","), this.getHostCard().getController(),
|
|
||||||
this.getHostCard(), null)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasParam("Player")) {
|
if (hasParam("Player")) {
|
||||||
final Player player = (Player) runParams.get(AbilityKey.Player);
|
if (!matchesValid(runParams.get(AbilityKey.Player), getParam("Player").split(","), getHostCard())) {
|
||||||
if (!player.isValid(getParam("Player").split(","), this.getHostCard().getController(), this.getHostCard(), null)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasParam("Activator")) {
|
if (hasParam("Activator")) {
|
||||||
final SpellAbility sa = (SpellAbility) runParams.get(AbilityKey.AbilityMana);
|
if (!matchesValid(runParams.get(AbilityKey.Activator), getParam("Activator").split(","), getHostCard())) {
|
||||||
if (sa == null) return false;
|
|
||||||
final Player activator = sa.getActivatingPlayer();
|
|
||||||
if (!activator.isValid(getParam("Activator").split(","), this.getHostCard().getController(), this.getHostCard(), null)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +106,7 @@ public class TriggerTapsForMana extends Trigger {
|
|||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||||
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card, AbilityKey.Player, AbilityKey.Produced);
|
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card, AbilityKey.Player, AbilityKey.Produced, AbilityKey.Activator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasLegalTargeting(sp, source)) {
|
if (!sp.isCopied() && !hasLegalTargeting(sp, source)) {
|
||||||
String str = source + " - [Couldn't add to stack, failed to target] - " + sp.getDescription();
|
String str = source + " - [Couldn't add to stack, failed to target] - " + sp.getDescription();
|
||||||
System.err.println(str + sp.getAllTargetChoices());
|
System.err.println(str + sp.getAllTargetChoices());
|
||||||
game.getGameLog().add(GameLogEntryType.STACK_ADD, str);
|
game.getGameLog().add(GameLogEntryType.STACK_ADD, str);
|
||||||
|
|||||||
@@ -238,6 +238,11 @@ public class GuiDesktop implements IGuiBase {
|
|||||||
new GuiDownloader(service, callback).show();
|
new GuiDownloader(service, callback).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshSkin() {
|
||||||
|
//todo refresh skin selector
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copyToClipboard(final String text) {
|
public void copyToClipboard(final String text) {
|
||||||
final StringSelection ss = new StringSelection(text);
|
final StringSelection ss = new StringSelection(text);
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ public enum CSubmenuDownloaders implements ICDoc {
|
|||||||
new GuiDownloader(new GuiDownloadPrices()).show();
|
new GuiDownloader(new GuiDownloadPrices()).show();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private final UiCommand cmdDownloadSkins = new UiCommand() {
|
||||||
|
@Override public void run() {
|
||||||
|
new GuiDownloader(new GuiDownloadSkins()).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
private final UiCommand cmdHowToPlay = new UiCommand() {
|
private final UiCommand cmdHowToPlay = new UiCommand() {
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
VSubmenuDownloaders.SINGLETON_INSTANCE.showHowToPlay();
|
VSubmenuDownloaders.SINGLETON_INSTANCE.showHowToPlay();
|
||||||
@@ -102,6 +107,7 @@ public enum CSubmenuDownloaders implements ICDoc {
|
|||||||
view.setImportPicturesCommand(cmdImportPictures);
|
view.setImportPicturesCommand(cmdImportPictures);
|
||||||
view.setHowToPlayCommand(cmdHowToPlay);
|
view.setHowToPlayCommand(cmdHowToPlay);
|
||||||
view.setDownloadPricesCommand(cmdDownloadPrices);
|
view.setDownloadPricesCommand(cmdDownloadPrices);
|
||||||
|
view.setDownloadSkinsCommand(cmdDownloadSkins);
|
||||||
view.setLicensingCommand(cmdLicensing);
|
view.setLicensingCommand(cmdLicensing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
|||||||
private final FLabel btnImportPictures = _makeButton(localizer.getMessage("btnImportPictures"));
|
private final FLabel btnImportPictures = _makeButton(localizer.getMessage("btnImportPictures"));
|
||||||
private final FLabel btnHowToPlay = _makeButton(localizer.getMessage("btnHowToPlay"));
|
private final FLabel btnHowToPlay = _makeButton(localizer.getMessage("btnHowToPlay"));
|
||||||
private final FLabel btnDownloadPrices = _makeButton(localizer.getMessage("btnDownloadPrices"));
|
private final FLabel btnDownloadPrices = _makeButton(localizer.getMessage("btnDownloadPrices"));
|
||||||
|
private final FLabel btnDownloadSkins = _makeButton(localizer.getMessage("btnDownloadSkins"));
|
||||||
private final FLabel btnLicensing = _makeButton(localizer.getMessage("btnLicensing"));
|
private final FLabel btnLicensing = _makeButton(localizer.getMessage("btnLicensing"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -102,6 +103,9 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
|||||||
pnlContent.add(btnDownloadPrices, constraintsBTN);
|
pnlContent.add(btnDownloadPrices, constraintsBTN);
|
||||||
pnlContent.add(_makeLabel(localizer.getMessage("lblDownloadPrices")), constraintsLBL);
|
pnlContent.add(_makeLabel(localizer.getMessage("lblDownloadPrices")), constraintsLBL);
|
||||||
|
|
||||||
|
pnlContent.add(btnDownloadSkins, constraintsBTN);
|
||||||
|
pnlContent.add(_makeLabel(localizer.getMessage("lblDownloadSkins")), constraintsLBL);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
String text = localizer.getMessage("lblYourVersionOfJavaIsTooOld");
|
String text = localizer.getMessage("lblYourVersionOfJavaIsTooOld");
|
||||||
@@ -178,6 +182,7 @@ public enum VSubmenuDownloaders implements IVSubmenu<CSubmenuDownloaders> {
|
|||||||
public void setHowToPlayCommand(UiCommand command) { btnHowToPlay.setCommand(command); }
|
public void setHowToPlayCommand(UiCommand command) { btnHowToPlay.setCommand(command); }
|
||||||
public void setDownloadPricesCommand(UiCommand command) { btnDownloadPrices.setCommand(command); }
|
public void setDownloadPricesCommand(UiCommand command) { btnDownloadPrices.setCommand(command); }
|
||||||
public void setLicensingCommand(UiCommand command) { btnLicensing.setCommand(command); }
|
public void setLicensingCommand(UiCommand command) { btnLicensing.setCommand(command); }
|
||||||
|
public void setDownloadSkinsCommand(UiCommand command) { btnDownloadSkins.setCommand(command); }
|
||||||
|
|
||||||
public void focusTopButton() {
|
public void focusTopButton() {
|
||||||
btnDownloadPics.requestFocusInWindow();
|
btnDownloadPics.requestFocusInWindow();
|
||||||
|
|||||||
@@ -1089,6 +1089,7 @@ public class FSkin {
|
|||||||
|
|
||||||
if (allSkins == null) { //initialize
|
if (allSkins == null) { //initialize
|
||||||
allSkins = new ArrayList<>();
|
allSkins = new ArrayList<>();
|
||||||
|
allSkins.add("Default");//init default
|
||||||
final List<String> skinDirectoryNames = getSkinDirectoryNames();
|
final List<String> skinDirectoryNames = getSkinDirectoryNames();
|
||||||
for (String skinDirectoryName : skinDirectoryNames) {
|
for (String skinDirectoryName : skinDirectoryNames) {
|
||||||
allSkins.add(WordUtil.capitalize(skinDirectoryName.replace('_', ' ')));
|
allSkins.add(WordUtil.capitalize(skinDirectoryName.replace('_', ' ')));
|
||||||
@@ -1101,7 +1102,7 @@ public class FSkin {
|
|||||||
|
|
||||||
// Non-default (preferred) skin name and dir.
|
// Non-default (preferred) skin name and dir.
|
||||||
preferredName = skinName.toLowerCase().replace(' ', '_');
|
preferredName = skinName.toLowerCase().replace(' ', '_');
|
||||||
preferredDir = ForgeConstants.SKINS_DIR + preferredName + "/";
|
preferredDir = preferredName.equals("default") ? ForgeConstants.DEFAULT_SKINS_DIR : ForgeConstants.CACHE_SKINS_DIR + preferredName + "/";
|
||||||
|
|
||||||
if (onInit) {
|
if (onInit) {
|
||||||
final File f = new File(preferredDir + ForgeConstants.SPLASH_BG_FILE);
|
final File f = new File(preferredDir + ForgeConstants.SPLASH_BG_FILE);
|
||||||
@@ -1371,7 +1372,7 @@ public class FSkin {
|
|||||||
public static List<String> getSkinDirectoryNames() {
|
public static List<String> getSkinDirectoryNames() {
|
||||||
final List<String> mySkins = new ArrayList<>();
|
final List<String> mySkins = new ArrayList<>();
|
||||||
|
|
||||||
final File dir = new File(ForgeConstants.SKINS_DIR);
|
final File dir = new File(ForgeConstants.CACHE_SKINS_DIR);
|
||||||
final String[] children = dir.list();
|
final String[] children = dir.list();
|
||||||
if (children == null) {
|
if (children == null) {
|
||||||
System.err.println("FSkin > can't find skins directory!");
|
System.err.println("FSkin > can't find skins directory!");
|
||||||
|
|||||||
@@ -304,14 +304,18 @@ public class GameSimulatorTest extends SimulationTestCase {
|
|||||||
Card manifestedCreature = findCardWithName(simGame, "");
|
Card manifestedCreature = findCardWithName(simGame, "");
|
||||||
assertNotNull(manifestedCreature);
|
assertNotNull(manifestedCreature);
|
||||||
|
|
||||||
SpellAbility unmanifestSA = findSAWithPrefix(manifestedCreature, "Unmanifest");
|
SpellAbility unmanifestSA = findSAWithPrefix(manifestedCreature.getAllPossibleAbilities(p, false), "Unmanifest");
|
||||||
assertNotNull(unmanifestSA);
|
assertNotNull(unmanifestSA);
|
||||||
assertEquals(2, manifestedCreature.getNetPower());
|
assertEquals(2, manifestedCreature.getNetPower());
|
||||||
assertFalse(manifestedCreature.hasKeyword("Flying"));
|
assertFalse(manifestedCreature.hasKeyword("Flying"));
|
||||||
|
|
||||||
GameSimulator sim2 = createSimulator(simGame, simGame.getPlayers().get(1));
|
GameSimulator sim2 = createSimulator(simGame, simGame.getPlayers().get(1));
|
||||||
sim2.simulateSpellAbility(unmanifestSA);
|
|
||||||
Game simGame2 = sim2.getSimulatedGameState();
|
Game simGame2 = sim2.getSimulatedGameState();
|
||||||
|
manifestedCreature = findCardWithName(simGame2, "");
|
||||||
|
unmanifestSA = findSAWithPrefix(manifestedCreature.getAllPossibleAbilities(simGame2.getPlayers().get(1), false), "Unmanifest");
|
||||||
|
|
||||||
|
sim2.simulateSpellAbility(unmanifestSA);
|
||||||
|
|
||||||
Card ornithopter = findCardWithName(simGame2, "Ornithopter");
|
Card ornithopter = findCardWithName(simGame2, "Ornithopter");
|
||||||
assertEquals(0, ornithopter.getNetPower());
|
assertEquals(0, ornithopter.getNetPower());
|
||||||
assertTrue(ornithopter.hasKeyword("Flying"));
|
assertTrue(ornithopter.hasKeyword("Flying"));
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ public class PlayerControllerForTests extends PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TargetChoices chooseNewTargetsFor(SpellAbility ability) {
|
public TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate<GameObject> filter, boolean optional) {
|
||||||
throw new IllegalStateException("Erring on the side of caution here...");
|
throw new IllegalStateException("Erring on the side of caution here...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -437,24 +437,32 @@ public class Forge implements ApplicationListener {
|
|||||||
try {
|
try {
|
||||||
endKeyInput(); //end key input before switching screens
|
endKeyInput(); //end key input before switching screens
|
||||||
ForgeAnimation.endAll(); //end all active animations before switching screens
|
ForgeAnimation.endAll(); //end all active animations before switching screens
|
||||||
try {
|
|
||||||
if(dispose)
|
|
||||||
ImageCache.disposeTexture();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// FIXME: This isn't supposed to be necessary, but disposeTexture crashes e.g. in Quest Tournaments on mobile, needs proper fixing.
|
|
||||||
System.err.println("Warning: caught an exception while trying to call ImageCache.disposeTexture() in setCurrentScreen.");
|
|
||||||
}
|
|
||||||
|
|
||||||
currentScreen = screen0;
|
currentScreen = screen0;
|
||||||
currentScreen.setSize(screenWidth, screenHeight);
|
currentScreen.setSize(screenWidth, screenHeight);
|
||||||
currentScreen.onActivate();
|
currentScreen.onActivate();
|
||||||
|
//keep Dscreens growing
|
||||||
|
if (Dscreens.size() > 3) {
|
||||||
|
for(int x = Dscreens.size(); x > 3; x--) {
|
||||||
|
Dscreens.removeLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* for checking only
|
||||||
|
if (!Dscreens.isEmpty()) {
|
||||||
|
int x = 0;
|
||||||
|
for(FScreen fScreen : Dscreens) {
|
||||||
|
System.out.println("Screen ["+x+"]: "+fScreen.toString());
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
System.out.println("---------------");
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
graphics.end();
|
graphics.end();
|
||||||
BugReporter.reportException(ex);
|
BugReporter.reportException(ex);
|
||||||
}
|
}
|
||||||
|
if(dispose)
|
||||||
|
ImageCache.disposeTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -267,6 +267,11 @@ public class GuiMobile implements IGuiBase {
|
|||||||
new GuiDownloader(service, callback).show();
|
new GuiDownloader(service, callback).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshSkin() {
|
||||||
|
//todo refresh skin selector
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copyToClipboard(final String text) {
|
public void copyToClipboard(final String text) {
|
||||||
Forge.getClipboard().setContents(text);
|
Forge.getClipboard().setContents(text);
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ public class FSkin {
|
|||||||
Forge.hdstart = false;
|
Forge.hdstart = false;
|
||||||
|
|
||||||
//ensure skins directory exists
|
//ensure skins directory exists
|
||||||
final FileHandle dir = Gdx.files.absolute(ForgeConstants.SKINS_DIR);
|
final FileHandle dir = Gdx.files.absolute(ForgeConstants.CACHE_SKINS_DIR);
|
||||||
if (!dir.exists() || !dir.isDirectory()) {
|
if (!dir.exists() || !dir.isDirectory()) {
|
||||||
//if skins directory doesn't exist, point to internal assets/skin directory instead for the sake of the splash screen
|
//if skins directory doesn't exist, point to internal assets/skin directory instead for the sake of the splash screen
|
||||||
preferredDir = Gdx.files.internal("fallback_skin");
|
preferredDir = Gdx.files.internal("fallback_skin");
|
||||||
@@ -106,6 +106,7 @@ public class FSkin {
|
|||||||
if (splashScreen != null) {
|
if (splashScreen != null) {
|
||||||
if (allSkins == null) { //initialize
|
if (allSkins == null) { //initialize
|
||||||
allSkins = new Array<>();
|
allSkins = new Array<>();
|
||||||
|
allSkins.add("Default"); //init default
|
||||||
final Array<String> skinDirectoryNames = getSkinDirectoryNames();
|
final Array<String> skinDirectoryNames = getSkinDirectoryNames();
|
||||||
for (final String skinDirectoryName : skinDirectoryNames) {
|
for (final String skinDirectoryName : skinDirectoryNames) {
|
||||||
allSkins.add(WordUtil.capitalize(skinDirectoryName.replace('_', ' ')));
|
allSkins.add(WordUtil.capitalize(skinDirectoryName.replace('_', ' ')));
|
||||||
@@ -115,7 +116,7 @@ public class FSkin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Non-default (preferred) skin name and dir.
|
// Non-default (preferred) skin name and dir.
|
||||||
preferredDir = Gdx.files.absolute(ForgeConstants.SKINS_DIR + preferredName);
|
preferredDir = Gdx.files.absolute(preferredName.equals("default") ? ForgeConstants.BASE_SKINS_DIR + preferredName : ForgeConstants.CACHE_SKINS_DIR + preferredName);
|
||||||
if (!preferredDir.exists() || !preferredDir.isDirectory()) {
|
if (!preferredDir.exists() || !preferredDir.isDirectory()) {
|
||||||
preferredDir.mkdirs();
|
preferredDir.mkdirs();
|
||||||
}
|
}
|
||||||
@@ -219,8 +220,12 @@ public class FSkin {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
textures.put(f1.path(), new Texture(f1));
|
textures.put(f1.path(), new Texture(f1));
|
||||||
|
Pixmap preferredIcons = new Pixmap(f1);
|
||||||
|
if (f2.exists()) {
|
||||||
textures.put(f2.path(), new Texture(f2));
|
textures.put(f2.path(), new Texture(f2));
|
||||||
Pixmap preferredIcons = new Pixmap(f2);
|
preferredIcons = new Pixmap(f2);
|
||||||
|
}
|
||||||
|
|
||||||
textures.put(f3.path(), new Texture(f3));
|
textures.put(f3.path(), new Texture(f3));
|
||||||
if (f6.exists()) {
|
if (f6.exists()) {
|
||||||
textures.put(f6.path(), new Texture(f6));
|
textures.put(f6.path(), new Texture(f6));
|
||||||
@@ -429,7 +434,7 @@ public class FSkin {
|
|||||||
public static Array<String> getSkinDirectoryNames() {
|
public static Array<String> getSkinDirectoryNames() {
|
||||||
final Array<String> mySkins = new Array<>();
|
final Array<String> mySkins = new Array<>();
|
||||||
|
|
||||||
final FileHandle dir = Gdx.files.absolute(ForgeConstants.SKINS_DIR);
|
final FileHandle dir = Gdx.files.absolute(ForgeConstants.CACHE_SKINS_DIR);
|
||||||
for (FileHandle skinFile : dir.list()) {
|
for (FileHandle skinFile : dir.list()) {
|
||||||
String skinName = skinFile.name();
|
String skinName = skinFile.name();
|
||||||
if (skinName.equalsIgnoreCase(".svn")) { continue; }
|
if (skinName.equalsIgnoreCase(".svn")) { continue; }
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import forge.download.GuiDownloadQuestImages;
|
|||||||
import forge.download.GuiDownloadSetPicturesLQ;
|
import forge.download.GuiDownloadSetPicturesLQ;
|
||||||
import forge.download.GuiDownloadService;
|
import forge.download.GuiDownloadService;
|
||||||
|
|
||||||
|
import forge.download.GuiDownloadSkins;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -76,7 +77,13 @@ public class FilesPage extends TabPage<SettingsScreen> {
|
|||||||
return new GuiDownloadPrices();
|
return new GuiDownloadPrices();
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
|
lstItems.addItem(new ContentDownloader(localizer.getMessage("btnDownloadSkins"),
|
||||||
|
localizer.getMessage("lblDownloadSkins")) {
|
||||||
|
@Override
|
||||||
|
protected GuiDownloadService createService() {
|
||||||
|
return new GuiDownloadSkins();
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
//storage locations
|
//storage locations
|
||||||
final StorageOption cardPicsOption = new StorageOption(localizer.getMessage("lblCardPicsLocation"), ForgeProfileProperties.getCardPicsDir()) {
|
final StorageOption cardPicsOption = new StorageOption(localizer.getMessage("lblCardPicsLocation"), ForgeProfileProperties.getCardPicsDir()) {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -510,10 +510,12 @@ public class SettingsPage extends TabPage<SettingsScreen> {
|
|||||||
public CustomSelectSetting(FPref pref0, String label0, String description0, Iterable<String> options0) {
|
public CustomSelectSetting(FPref pref0, String label0, String description0, Iterable<String> options0) {
|
||||||
super(pref0, label0 + ":", description0);
|
super(pref0, label0 + ":", description0);
|
||||||
|
|
||||||
|
if (options0 != null) {
|
||||||
for (String option : options0) {
|
for (String option : options0) {
|
||||||
options.add(option);
|
options.add(option);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public <E extends Enum<E>> CustomSelectSetting(FPref pref0, String label0, String description0, Class<E> enumData) {
|
public <E extends Enum<E>> CustomSelectSetting(FPref pref0, String label0, String description0, Class<E> enumData) {
|
||||||
super(pref0, label0 + ":", description0);
|
super(pref0, label0 + ":", description0);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,5 @@ Types:Scheme
|
|||||||
T:Mode$ SetInMotion | ValidCard$ Card.Self | Execute$ DarkEffect | TriggerZones$ Command | TriggerDescription$ When you set this scheme in motion, until your next turn, whenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
T:Mode$ SetInMotion | ValidCard$ Card.Self | Execute$ DarkEffect | TriggerZones$ Command | TriggerDescription$ When you set this scheme in motion, until your next turn, whenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
||||||
SVar:DarkEffect:DB$ Effect | Name$ Dark Power Scheme | Duration$ UntilYourNextTurn | Triggers$ DarkPower | SVars$ DarkMana
|
SVar:DarkEffect:DB$ Effect | Name$ Dark Power Scheme | Duration$ UntilYourNextTurn | Triggers$ DarkPower | SVars$ DarkMana
|
||||||
SVar:DarkPower:Mode$ TapsForMana | ValidCard$ Land | Execute$ DarkMana | TriggerZones$ Command | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
SVar:DarkPower:Mode$ TapsForMana | ValidCard$ Land | Execute$ DarkMana | TriggerZones$ Command | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
||||||
SVar:DarkMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer
|
SVar:DarkMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
|
||||||
SVar:Picture:https://downloads.cardforge.org/images/cards/ARC/A Display of My Dark Power.full.jpg
|
|
||||||
Oracle:When you set this scheme in motion, until your next turn, whenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
Oracle:When you set this scheme in motion, until your next turn, whenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
||||||
|
|||||||
10
forge-gui/res/cardsfolder/b/bounty_of_skemfar.txt
Normal file
10
forge-gui/res/cardsfolder/b/bounty_of_skemfar.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Name:Bounty of Skemfar
|
||||||
|
ManaCost:2 G
|
||||||
|
Types:Sorcery
|
||||||
|
A:SP$ Dig | DigNum$ 6 | Reveal$ True | ChangeNum$ 1 | Optional$ True | ChangeValid$ Land | DestinationZone$ Battlefield | Tapped$ True | DestinationZone2$ Library | LibraryPosition2$ 0 | SkipReorder$ True | RememberChanged$ True | SubAbility$ DBElf | SpellDescription$ Reveal the top six cards of your library. You may put a land card from among them onto the battlefield tapped and an Elf card from among them into your hand. Put the rest on the bottom of your library in a random order. | StackDescription$ SpellDescription
|
||||||
|
SVar:DBElf:DB$ Dig | DigNum$ X | References$ X,Y | ChangeNum$ 1 | Optional$ True | ChangeValid$ Elf | RestRandomOrder$ True | SubAbility$ DBCleanup | StackDescription$ None
|
||||||
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
|
SVar:X:Count$Compare Y GE1.5.6
|
||||||
|
SVar:Y:Remembered$Amount
|
||||||
|
DeckHints:Type$Elf
|
||||||
|
Oracle:Reveal the top six cards of your library. You may put a land card from among them onto the battlefield tapped and an Elf card from among them into your hand. Put the rest on the bottom of your library in a random order.
|
||||||
@@ -2,7 +2,7 @@ Name:Brain in a Jar
|
|||||||
ManaCost:2
|
ManaCost:2
|
||||||
Types:Artifact
|
Types:Artifact
|
||||||
A:AB$ PutCounter | Cost$ 1 T | CounterType$ CHARGE | CounterNum$ 1 | SubAbility$ DBCast | SpellDescription$ Put a charge counter on Brain in a Jar, then you may cast an instant or sorcery card with converted mana cost equal to the number of charge counters on Brain in a Jar from your hand without paying its mana cost.
|
A:AB$ PutCounter | Cost$ 1 T | CounterType$ CHARGE | CounterNum$ 1 | SubAbility$ DBCast | SpellDescription$ Put a charge counter on Brain in a Jar, then you may cast an instant or sorcery card with converted mana cost equal to the number of charge counters on Brain in a Jar from your hand without paying its mana cost.
|
||||||
SVar:DBCast:DB$ Play | ValidZone$ Hand | Valid$ Instant.YouOwn+cmcEQY,Sorcery.YouOwn+cmcEQY | Controller$ You | WithoutManaCost$ True | Optional$ True | Amount$ 1 | References$ Y
|
SVar:DBCast:DB$ Play | ValidZone$ Hand | Valid$ Instant.YouOwn,Sorcery.YouOwn| ValidSA$ Spell.cmcEQY | Controller$ You | WithoutManaCost$ True | Optional$ True | Amount$ 1 | References$ Y
|
||||||
A:AB$ Scry | Cost$ 3 T SubCounter<X/CHARGE> | ScryNum$ X | References$ X | AILogic$ BrainJar | SpellDescription$ Scry X.
|
A:AB$ Scry | Cost$ 3 T SubCounter<X/CHARGE> | ScryNum$ X | References$ X | AILogic$ BrainJar | SpellDescription$ Scry X.
|
||||||
SVar:X:Count$xPaid
|
SVar:X:Count$xPaid
|
||||||
SVar:Y:Count$CardCounters.CHARGE
|
SVar:Y:Count$CardCounters.CHARGE
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ Types:Artifact
|
|||||||
K:ETBReplacement:Other:ChooseColor
|
K:ETBReplacement:Other:ChooseColor
|
||||||
SVar:ChooseColor:DB$ ChooseColor | Defined$ You | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a color.
|
SVar:ChooseColor:DB$ ChooseColor | Defined$ You | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a color.
|
||||||
S:Mode$ Continuous | Affected$ Creature.ChosenColor+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control of the chosen color get +1/+1.
|
S:Mode$ Continuous | Affected$ Creature.ChosenColor+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control of the chosen color get +1/+1.
|
||||||
T:Mode$ TapsForMana | ValidCard$ Land | Produced$ ChosenColor | NoTapCheck$ True | Player$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a land's ability adds one or more mana of the chosen color, add one additional mana of that color.
|
T:Mode$ TapsForMana | ValidCard$ Land | Produced$ ChosenColor | NoTapCheck$ True | Player$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a land’s ability causes you to add one or more mana of the chosen color, add one additional mana of that color.
|
||||||
SVar:TrigMana:DB$ Mana | Produced$ Chosen | Amount$ 1 | Defined$ TriggeredPlayer
|
SVar:TrigMana:DB$ Mana | Produced$ Chosen | Amount$ 1 | Defined$ You
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/caged_sun.jpg
|
Oracle:As Caged Sun enters the battlefield, choose a color.\nCreatures you control of the chosen color get +1/+1.\nWhenever a land’s ability causes you to add one or more mana of the chosen color, add one additional mana of that color.
|
||||||
Oracle:As Caged Sun enters the battlefield, choose a color.\nCreatures you control of the chosen color get +1/+1.\nWhenever a land's ability adds one or more mana of the chosen color, add one additional mana of that color.
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ SVar:DBEffect:DB$ Effect | ReplacementEffects$ RepCurse | SVars$ ProduceColorles
|
|||||||
SVar:TrigRamp:Mode$ TapsForMana | ValidCard$ Mountain | Execute$ TrigMana | Static$ True | TriggerZones$ Command | TriggerDescription$ Whenever a player taps a Mountain for mana, that player adds {R}.
|
SVar:TrigRamp:Mode$ TapsForMana | ValidCard$ Mountain | Execute$ TrigMana | Static$ True | TriggerZones$ Command | TriggerDescription$ Whenever a player taps a Mountain for mana, that player adds {R}.
|
||||||
SVar:TrigMana:DB$ Mana | Produced$ R | Amount$ 1 | Defined$ TriggeredCardController
|
SVar:TrigMana:DB$ Mana | Produced$ R | Amount$ 1 | Defined$ TriggeredCardController
|
||||||
SVar:STPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.Red | AddPower$ 1 | AddToughness$ 1
|
SVar:STPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.Red | AddPower$ 1 | AddToughness$ 1
|
||||||
SVar:RepCurse:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Mountain | ManaReplacement$ ProduceColorless | Description$ If a player taps a Mountain for mana, that Mountain produces colorless mana instead of any other type.
|
SVar:RepCurse:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Mountain | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceColorless | Description$ If a player taps a Mountain for mana, that Mountain produces colorless mana instead of any other type.
|
||||||
SVar:ProduceColorless:R->1 & B->1 & U->1 & G->1 & W->1
|
SVar:ProduceColorless:DB$ ReplaceMana | ReplaceType$ C
|
||||||
SVar:STCurse:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.Red | AddPower$ -1 | AddToughness$ -1
|
SVar:STCurse:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.Red | AddPower$ -1 | AddToughness$ -1
|
||||||
SVar:X:Count$Valid Permanent
|
SVar:X:Count$Valid Permanent
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ ManaCost:2 B
|
|||||||
Types:Snow Creature Shade
|
Types:Snow Creature Shade
|
||||||
PT:1/1
|
PT:1/1
|
||||||
K:Flying
|
K:Flying
|
||||||
A:AB$ Pump | Cost$ S | Defined$ Self | +1 | NumDef$ +1 | SpellDescription$ CARDNAME gets +1/+1 until end of turn.
|
A:AB$ Pump | Cost$ S | Defined$ Self | NumAtt$ +1 | NumDef$ +1 | SpellDescription$ CARDNAME gets +1/+1 until end of turn.
|
||||||
# AI can now use snow mana to pay for activated abilities.
|
DeckNeeds:Type$Snow
|
||||||
AI:RemoveDeck:Random
|
|
||||||
DeckHints:Type$Snow
|
|
||||||
Oracle:Flying\n{S}: Chilling Shade gets +1/+1 until end of turn. ({S} can be paid with one mana from a snow permanent.)
|
Oracle:Flying\n{S}: Chilling Shade gets +1/+1 until end of turn. ({S} can be paid with one mana from a snow permanent.)
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ Name:Contamination
|
|||||||
ManaCost:2 B
|
ManaCost:2 B
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
K:UpkeepCost:Sac<1/Creature>
|
K:UpkeepCost:Sac<1/Creature>
|
||||||
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ManaReplacement$ ProduceB | Description$ If a land is tapped for mana, it produces {B} instead of any other type and amount.
|
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceB | Description$ If a land is tapped for mana, it produces {B} instead of any other type and amount.
|
||||||
SVar:ProduceB:Any->B
|
SVar:ProduceB:DB$ ReplaceMana | ReplaceMana$ B
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/contamination.jpg
|
|
||||||
Oracle:At the beginning of your upkeep, sacrifice Contamination unless you sacrifice a creature.\nIf a land is tapped for mana, it produces {B} instead of any other type and amount.
|
Oracle:At the beginning of your upkeep, sacrifice Contamination unless you sacrifice a creature.\nIf a land is tapped for mana, it produces {B} instead of any other type and amount.
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ ManaCost:3 B
|
|||||||
Types:Creature Spirit
|
Types:Creature Spirit
|
||||||
PT:2/2
|
PT:2/2
|
||||||
K:Extort
|
K:Extort
|
||||||
T:Mode$ TapsForMana | ValidCard$ Swamp.YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a Swamp for mana, add an additional {B}.
|
T:Mode$ TapsForMana | ValidCard$ Swamp | Activator$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a Swamp for mana, add an additional {B}.
|
||||||
SVar:TrigMana:DB$Mana | Produced$ B | Amount$ 1
|
SVar:TrigMana:DB$ Mana | Produced$ B | Amount$ 1
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/crypt_ghast.jpg
|
|
||||||
Oracle:Extort (Whenever you cast a spell, you may pay {W/B}. If you do, each opponent loses 1 life and you gain that much life.)\nWhenever you tap a Swamp for mana, add an additional {B}.
|
Oracle:Extort (Whenever you cast a spell, you may pay {W/B}. If you do, each opponent loses 1 life and you gain that much life.)\nWhenever you tap a Swamp for mana, add an additional {B}.
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
Name:Damping Sphere
|
Name:Damping Sphere
|
||||||
ManaCost:2
|
ManaCost:2
|
||||||
Types:Artifact
|
Types:Artifact
|
||||||
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ManaAmount$ GE2 | ManaReplacement$ ProduceC | Description$ If a land is tapped for two or more mana, it produces {C} instead of any other type and amount.
|
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ValidAbility$ Activated.hasTapCost | ManaAmount$ GE2 | ReplaceWith$ ProduceC | Description$ If a land is tapped for two or more mana, it produces {C} instead of any other type and amount.
|
||||||
SVar:ProduceC:Any->C
|
SVar:ProduceC:DB$ ReplaceMana | ReplaceMana$ C
|
||||||
S:Mode$ RaiseCost | Activator$ Player | Type$ Spell | Amount$ X | AffectedAmount$ True | Description$ Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn.
|
S:Mode$ RaiseCost | Activator$ Player | Type$ Spell | Amount$ X | AffectedAmount$ True | Description$ Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn.
|
||||||
SVar:X:Count$ThisTurnCast_Card.YouCtrl
|
SVar:X:Count$ThisTurnCast_Card.YouCtrl
|
||||||
SVar:NonStackingEffect:True
|
SVar:NonStackingEffect:True
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/damping_sphere.jpg
|
|
||||||
Oracle:If a land is tapped for two or more mana, it produces {C} instead of any other type and amount.\nEach spell a player casts costs {1} more to cast for each other spell that player has cast this turn.
|
Oracle:If a land is tapped for two or more mana, it produces {C} instead of any other type and amount.\nEach spell a player casts costs {1} more to cast for each other spell that player has cast this turn.
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ Name:Deep Water
|
|||||||
ManaCost:U U
|
ManaCost:U U
|
||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
A:AB$ Effect | Cost$ U | ReplacementEffects$ ReplaceU | SVars$ ProduceU | SpellDescription$ Until end of turn, if you tap a land you control for mana, it produces {U} instead of any other type.
|
A:AB$ Effect | Cost$ U | ReplacementEffects$ ReplaceU | SVars$ ProduceU | SpellDescription$ Until end of turn, if you tap a land you control for mana, it produces {U} instead of any other type.
|
||||||
SVar:ReplaceU:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Land.YouCtrl | ManaReplacement$ ProduceU | Description$ If you tap a land you control for mana, it produces U instead of any other type.
|
SVar:ReplaceU:Event$ ProduceMana | ActiveZones$ Command | ValidPlayer$ You | ValidCard$ Land.YouCtrl | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceU | Description$ If you tap a land you control for mana, it produces U instead of any other type.
|
||||||
SVar:ProduceU:C->U & B->U & R->U & G->U & W->U
|
SVar:ProduceU:DB$ ReplaceMana | ReplaceType$ U
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/deep_water.jpg
|
|
||||||
Oracle:{U}: Until end of turn, if you tap a land you control for mana, it produces {U} instead of any other type.
|
Oracle:{U}: Until end of turn, if you tap a land you control for mana, it produces {U} instead of any other type.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
Name:Detonate
|
Name:Detonate
|
||||||
ManaCost:X R
|
ManaCost:X R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ Destroy | Cost$ X R | ValidTgts$ Artifact | ValidTgtsWithoutManaCost$ Artifact.cmcEQ0 | TgtPrompt$ Select target artifact | NoRegen$ True | SubAbility$ DBDamage | References$ X | SpellDescription$ Destroy target artifact with converted mana cost X. It can't be regenerated. CARDNAME deals X damage to that artifact's controller.
|
A:SP$ Destroy | Cost$ X R | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | NoRegen$ True | SubAbility$ DBDamage | References$ X | SpellDescription$ Destroy target artifact with converted mana cost X. It can't be regenerated. CARDNAME deals X damage to that artifact's controller.
|
||||||
SVar:DBDamage:DB$DealDamage | Defined$ TargetedController | NumDmg$ X | References$ X
|
SVar:DBDamage:DB$DealDamage | Defined$ TargetedController | NumDmg$ X | References$ X
|
||||||
SVar:X:Targeted$CardManaCost
|
SVar:X:Count$xPaid
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/detonate.jpg
|
|
||||||
Oracle:Destroy target artifact with converted mana cost X. It can't be regenerated. Detonate deals X damage to that artifact's controller.
|
Oracle:Destroy target artifact with converted mana cost X. It can't be regenerated. Detonate deals X damage to that artifact's controller.
|
||||||
|
|||||||
@@ -3,6 +3,5 @@ ManaCost:3 G G
|
|||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
K:Flash
|
K:Flash
|
||||||
T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
||||||
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer
|
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/dictate_of_karametra.jpg
|
|
||||||
Oracle:Flash\nWhenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
Oracle:Flash\nWhenever a player taps a land for mana, that player adds one mana of any type that land produced.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user