diff --git a/.gitattributes b/.gitattributes
index a30e915ca2a..abfd68943bb 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -29,6 +29,7 @@ forge-ai/src/main/java/forge/ai/LobbyPlayerAi.java -text
forge-ai/src/main/java/forge/ai/PlayerControllerAi.java -text
forge-ai/src/main/java/forge/ai/SpellAbilityAi.java -text
forge-ai/src/main/java/forge/ai/SpellApiToAi.java -text
+forge-ai/src/main/java/forge/ai/ability/ActivateAbilityAi.java -text
forge-ai/src/main/java/forge/ai/ability/AddPhaseAi.java -text
forge-ai/src/main/java/forge/ai/ability/AddTurnAi.java svneol=native#text/plain
forge-ai/src/main/java/forge/ai/ability/AlwaysPlayAi.java -text
@@ -292,6 +293,7 @@ forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java -text
forge-game/src/main/java/forge/game/ability/SpellApiBased.java -text
forge-game/src/main/java/forge/game/ability/StaticAbilityApiBased.java -text
forge-game/src/main/java/forge/game/ability/effects/AbandonEffect.java -text
+forge-game/src/main/java/forge/game/ability/effects/ActivateAbilityEffect.java -text
forge-game/src/main/java/forge/game/ability/effects/AddPhaseEffect.java -text
forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java -text
forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java -text
@@ -4424,6 +4426,7 @@ forge-gui/res/cardsfolder/d/dragonspeaker_shaman.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/dragonstalker.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/dragonstorm.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/drain_life.txt -text
+forge-gui/res/cardsfolder/d/drain_power.txt -text
forge-gui/res/cardsfolder/d/drain_the_well.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/draining_whelk.txt -text
forge-gui/res/cardsfolder/d/drainpipe_vermin.txt -text
diff --git a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java
index 75af5ac1ee0..832561e7e94 100644
--- a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java
+++ b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java
@@ -14,6 +14,7 @@ public enum SpellApiToAi {
static {
apiToClass.put(ApiType.Abandon, AlwaysPlayAi.class);
+ apiToClass.put(ApiType.ActivateAbility, ActivateAbilityAi.class);
apiToClass.put(ApiType.AddOrRemoveCounter, CountersPutOrRemoveAi.class);
apiToClass.put(ApiType.AddPhase, AddPhaseAi.class);
apiToClass.put(ApiType.AddTurn, AddTurnAi.class);
diff --git a/forge-ai/src/main/java/forge/ai/ability/ActivateAbilityAi.java b/forge-ai/src/main/java/forge/ai/ability/ActivateAbilityAi.java
new file mode 100644
index 00000000000..8d32835d5b2
--- /dev/null
+++ b/forge-ai/src/main/java/forge/ai/ability/ActivateAbilityAi.java
@@ -0,0 +1,100 @@
+package forge.ai.ability;
+
+import forge.ai.SpellAbilityAi;
+import forge.game.ability.AbilityUtils;
+import forge.game.card.Card;
+import forge.game.card.CardLists;
+import forge.game.player.Player;
+import forge.game.spellability.SpellAbility;
+import forge.game.spellability.TargetRestrictions;
+import forge.game.zone.ZoneType;
+import forge.util.MyRandom;
+
+import java.util.List;
+import java.util.Random;
+
+public class ActivateAbilityAi extends SpellAbilityAi {
+
+ @Override
+ protected boolean canPlayAI(Player ai, SpellAbility sa) {
+ // AI cannot use this properly until he can use SAs during Humans turn
+
+ final TargetRestrictions tgt = sa.getTargetRestrictions();
+ final Card source = sa.getHostCard();
+ final Player opp = ai.getOpponent();
+ final Random r = MyRandom.getRandom();
+ boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
+
+ List list = CardLists.getType(opp.getCardsIn(ZoneType.Battlefield), sa.getParamOrDefault("Type", "Card"));
+ if (list.isEmpty()) {
+ return false;
+ }
+
+ if (tgt == null) {
+ final List defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
+
+ if (!defined.contains(opp)) {
+ return false;
+ }
+ } else {
+ sa.resetTargets();
+ sa.getTargets().add(opp);
+ }
+
+ return randomReturn;
+ }
+
+ @Override
+ protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
+ final Player opp = ai.getOpponent();
+
+ final TargetRestrictions tgt = sa.getTargetRestrictions();
+ final Card source = sa.getHostCard();
+
+ if (null == tgt) {
+ if (mandatory) {
+ return true;
+ } else {
+ final List defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
+
+ if (!defined.contains(opp)) {
+ return false;
+ }
+ }
+
+ return true;
+ } else {
+ sa.resetTargets();
+ sa.getTargets().add(opp);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean chkAIDrawback(SpellAbility sa, Player ai) {
+ // AI cannot use this properly until he can use SAs during Humans turn
+ final TargetRestrictions tgt = sa.getTargetRestrictions();
+ final Card source = sa.getHostCard();
+
+ boolean randomReturn = true;
+
+ if (tgt == null) {
+ final List defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
+
+ if (defined.contains(ai)) {
+ return false;
+ }
+ } else {
+ sa.resetTargets();
+ sa.getTargets().add(ai.getOpponent());
+ }
+
+ return randomReturn;
+ }
+
+ @Override
+ public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List spells) {
+ return spells.get(0);
+ }
+}
diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java
index 001e725ac54..029f9e426e3 100644
--- a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java
+++ b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java
@@ -30,8 +30,9 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
@Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
return canPlayAI(aiPlayer, sa);
- }
-
+ }
+
+ @Override
public SpellAbility chooseSingleSpellAbility(Player player, SpellAbility sa, List spells) {
final String logic = sa.getParam("AILogic");
if ("Random".equals(logic)) {
diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeExchangeAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeExchangeAi.java
index 241ed739733..4ded619a314 100644
--- a/forge-ai/src/main/java/forge/ai/ability/LifeExchangeAi.java
+++ b/forge-ai/src/main/java/forge/ai/ability/LifeExchangeAi.java
@@ -60,5 +60,34 @@ public class LifeExchangeAi extends SpellAbilityAi {
return ((r.nextFloat() < .6667) && chance);
}
+ /**
+ *
+ * exchangeLifeDoTriggerAINoCost.
+ *
+ * @param sa
+ * a {@link forge.game.spellability.SpellAbility} object.
+ * @param mandatory
+ * a boolean.
+ * @param af
+ * a {@link forge.game.ability.AbilityFactory} object.
+ *
+ * @return a boolean.
+ */
+ @Override
+ protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa,
+ final boolean mandatory) {
+
+ final TargetRestrictions tgt = sa.getTargetRestrictions();
+ Player opp = ai.getOpponent();
+ if (tgt != null) {
+ sa.resetTargets();
+ if (sa.canTarget(opp) && (mandatory || ai.getLife() < opp.getLife())) {
+ sa.getTargets().add(opp);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java
index 7fb41ee6364..dc21c12e0c4 100644
--- a/forge-game/src/main/java/forge/game/ability/ApiType.java
+++ b/forge-game/src/main/java/forge/game/ability/ApiType.java
@@ -13,6 +13,7 @@ import java.util.TreeMap;
*/
public enum ApiType {
Abandon (AbandonEffect.class),
+ ActivateAbility (ActivateAbilityEffect.class),
AddOrRemoveCounter (CountersPutOrRemoveEffect.class),
AddPhase (AddPhaseEffect.class),
AddTurn (AddTurnEffect.class),
diff --git a/forge-game/src/main/java/forge/game/ability/effects/ActivateAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ActivateAbilityEffect.java
new file mode 100644
index 00000000000..c328b9b8948
--- /dev/null
+++ b/forge-game/src/main/java/forge/game/ability/effects/ActivateAbilityEffect.java
@@ -0,0 +1,58 @@
+package forge.game.ability.effects;
+
+import forge.game.ability.SpellAbilityEffect;
+import forge.game.card.Card;
+import forge.game.card.CardLists;
+import forge.game.player.Player;
+import forge.game.spellability.SpellAbility;
+import forge.game.spellability.TargetRestrictions;
+import forge.game.zone.ZoneType;
+import forge.util.Lang;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+public class ActivateAbilityEffect extends SpellAbilityEffect {
+ @Override
+ protected String getStackDescription(SpellAbility sa) {
+ final StringBuilder sb = new StringBuilder();
+
+ final List tgtPlayers = getTargetPlayers(sa);
+
+ sb.append(StringUtils.join(tgtPlayers, ", "));
+ sb.append(" activates ");
+ sb.append(Lang.nounWithAmount(1, sa.hasParam("ManaAbility") ? "mana ability" : "ability"));
+ sb.append(" of each ").append(sa.getParamOrDefault("Type", "Card"));
+ sb.append(" he or she controls.");
+
+ return sb.toString();
+ }
+
+ @Override
+ public void resolve(SpellAbility sa) {
+ final TargetRestrictions tgt = sa.getTargetRestrictions();
+ final boolean isManaAb = sa.hasParam("ManaAbility");
+ // TODO: improve ai and fix corner cases
+
+ for (final Player p : getTargetPlayers(sa)) {
+ if ((tgt == null) || p.canBeTargetedBy(sa)) {
+ List list = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), sa.getParamOrDefault("Type", "Card"));
+ for (Card c : list) {
+ List possibleAb = Lists.newArrayList(c.getAllPossibleAbilities(p, true));
+ if (isManaAb) {
+ possibleAb.retainAll(c.getManaAbility());
+ }
+ if (possibleAb.isEmpty()) {
+ continue;
+ }
+ SpellAbility manaAb = p.getController().chooseSingleSpellForEffect(possibleAb, sa, "Choose a mana ability:");
+ p.getController().playChosenSpellAbility(manaAb);
+ }
+ }
+ }
+ }
+
+}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DrainManaEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DrainManaEffect.java
index 5dd9f7a42cd..a021e6f848b 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DrainManaEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DrainManaEffect.java
@@ -1,9 +1,11 @@
package forge.game.ability.effects;
import forge.game.ability.SpellAbilityEffect;
+import forge.game.mana.Mana;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
+
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@@ -27,7 +29,12 @@ public class DrainManaEffect extends SpellAbilityEffect {
for (final Player p : getTargetPlayers(sa)) {
if ((tgt == null) || p.canBeTargetedBy(sa)) {
- p.getManaPool().clearPool(false);
+ List drained = p.getManaPool().clearPool(false);
+ if (sa.hasParam("DrainMana")) {
+ for (Mana mana : drained) {
+ sa.getActivatingPlayer().getManaPool().addMana(mana);
+ }
+ }
}
}
}
diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java
index 741229ed886..65307a895b1 100644
--- a/forge-game/src/main/java/forge/game/mana/ManaPool.java
+++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java
@@ -125,15 +125,15 @@ public class ManaPool implements Iterable {
* @return - the amount of mana removed this way
*
*/
- public final int clearPool(boolean isEndOfPhase) {
+ public final List clearPool(boolean isEndOfPhase) {
// isEndOfPhase parameter: true = end of phase, false = mana drain effect
- if (floatingMana.isEmpty()) { return 0; }
+ List cleared = new ArrayList();
+ if (floatingMana.isEmpty()) { return cleared; }
if (isEndOfPhase && owner.getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.manapoolsDontEmpty)) {
- return 0;
+ return cleared;
}
- int numRemoved = 0;
boolean keepGreenMana = isEndOfPhase && owner.hasKeyword("Green mana doesn't empty from your mana pool as steps and phases end.");
boolean convertToColorless = owner.hasKeyword("Convert unused mana to Colorless");
@@ -153,11 +153,12 @@ public class ManaPool implements Iterable {
pMana.add(mana);
}
}
+ floatingMana.get(b).removeAll(pMana);
if (convertToColorless) {
- floatingMana.get(b).removeAll(pMana);
convertManaColor(b, MagicColor.COLORLESS);
+ floatingMana.get(b).addAll(pMana);
} else {
- numRemoved += floatingMana.get(b).size() - pMana.size();
+ cleared.addAll(floatingMana.get(b));
floatingMana.get(b).clear();
floatingMana.putAll(b, pMana);
}
@@ -166,14 +167,14 @@ public class ManaPool implements Iterable {
if (convertToColorless) {
convertManaColor(b, MagicColor.COLORLESS);
} else {
- numRemoved += floatingMana.get(b).size();
+ cleared.addAll(floatingMana.get(b));
floatingMana.get(b).clear();
}
}
}
owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Cleared, null));
- return numRemoved;
+ return cleared;
}
private void convertManaColor(final byte originalColor, final byte toColor) {
diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java
index b998d1959df..267bbf6488b 100644
--- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java
+++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java
@@ -410,7 +410,7 @@ public class PhaseHandler implements java.io.Serializable, IGameStateObject {
}
for (Player p : game.getPlayers()) {
- int burn = p.getManaPool().clearPool(true);
+ int burn = p.getManaPool().clearPool(true).size();
boolean manaBurns = game.getRules().hasManaBurn();
if (manaBurns) {
diff --git a/forge-gui/res/cardsfolder/d/drain_power.txt b/forge-gui/res/cardsfolder/d/drain_power.txt
new file mode 100644
index 00000000000..f1acc3ae21f
--- /dev/null
+++ b/forge-gui/res/cardsfolder/d/drain_power.txt
@@ -0,0 +1,7 @@
+Name:Drain Power
+ManaCost:U U
+Types:Sorcery
+A:SP$ ActivateAbility | Cost$ U U | ValidTgts$ Player | Type$ Land | ManaAbility$ True | SubAbility$ DBDrainMana | SpellDescription$ Target player activates a mana ability of each land he or she controls. Then put all mana from that player's mana pool into yours.
+SVar:DBDrainMana:DB$ DrainMana | Defined$ Targeted | DrainMana$ True
+SVar:Picture:http://www.wizards.com/global/images/magic/general/drain_power.jpg
+Oracle:Target player activates a mana ability of each land he or she controls. Then put all mana from that player's mana pool into yours.