diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
index a96ee50cd61..b88caf8c09a 100644
--- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
+++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
@@ -494,7 +494,7 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
- public TargetChoices chooseNewTargetsFor(SpellAbility ability) {
+ public TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate filter, boolean optional) {
// AI currently can't do this. But when it can it will need to be based on Ability API
return null;
}
@@ -978,9 +978,13 @@ public class PlayerControllerAi extends PlayerController {
if (sa.isSpell()) {
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 targets can't be done, remove copy from existence
if (sa.isSpell()) {
sa.getHostCard().ceaseToExist();
}
diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java
index dee21c7340b..36e8a3839d2 100644
--- a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java
+++ b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java
@@ -126,7 +126,7 @@ public class CountersPutAi extends SpellAbilityAi {
Card choice = null;
final String type = sa.getParam("CounterType");
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
- final boolean divided = sa.hasParam("DividedAsYouChoose");
+ final boolean divided = sa.isDividedAsYouChoose();
final String logic = sa.getParamOrDefault("AILogic", "");
PhaseHandler ph = ai.getGame().getPhaseHandler();
@@ -398,16 +398,15 @@ public class CountersPutAi extends SpellAbilityAi {
}
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
- if (sa.usesTargeting() && abTgt.getMinTargets(source, sa) < 2) {
+ if (sa.usesTargeting() && sa.getMinTargets() < 2) {
if (ComputerUtilCard.canPumpAgainstRemoval(ai, sa)) {
Card c = sa.getTargets().getFirstTargetedCard();
if (sa.getTargets().size() > 1) {
sa.resetTargets();
sa.getTargets().add(c);
}
- abTgt.addDividedAllocation(sa.getTargetCard(), amount);
+ sa.addDividedAllocation(sa.getTargetCard(), amount);
return true;
} else {
return false;
@@ -462,7 +461,6 @@ public class CountersPutAi extends SpellAbilityAi {
}
if (sourceName.equals("Abzan Charm")) {
- final TargetRestrictions abTgt = sa.getTargetRestrictions();
// specific AI for instant with distribute two +1/+1 counters
ComputerUtilCard.sortByEvaluateCreature(list);
// maximise the number of targets
@@ -472,11 +470,11 @@ public class CountersPutAi extends SpellAbilityAi {
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, i, i,
Lists.newArrayList())) {
sa.getTargets().add(c);
- abTgt.addDividedAllocation(c, i);
+ sa.addDividedAllocation(c, i);
left -= i;
}
- if (left < i || sa.getTargets().size() == abTgt.getMaxTargets(source, sa)) {
- abTgt.addDividedAllocation(sa.getTargets().getFirstTargetedCard(), left + i);
+ if (left < i || sa.getTargets().size() == sa.getMaxTargets()) {
+ sa.addDividedAllocation(sa.getTargets().getFirstTargetedCard(), left + i);
left = 0;
break;
}
@@ -542,7 +540,7 @@ public class CountersPutAi extends SpellAbilityAi {
list.remove(choice);
sa.getTargets().add(choice);
if (divided) {
- sa.getTargetRestrictions().addDividedAllocation(choice, amount);
+ sa.addDividedAllocation(choice, amount);
break;
}
choice = null;
@@ -609,7 +607,7 @@ public class CountersPutAi extends SpellAbilityAi {
final String logic = sa.getParamOrDefault("AILogic", "");
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 boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
@@ -672,7 +670,7 @@ public class CountersPutAi extends SpellAbilityAi {
list.remove(choice);
sa.getTargets().add(choice);
if (divided) {
- sa.getTargetRestrictions().addDividedAllocation(choice, amount);
+ sa.addDividedAllocation(choice, amount);
break;
}
}
@@ -690,7 +688,7 @@ public class CountersPutAi extends SpellAbilityAi {
CardCollection list;
final String type = sa.getParam("CounterType");
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);
int left = amount;
@@ -818,12 +816,11 @@ public class CountersPutAi extends SpellAbilityAi {
}
}
if (choice != null && divided) {
- final TargetRestrictions abTgt = sa.getTargetRestrictions();
int alloc = Math.max(amount / totalTargets, 1);
- if (sa.getTargets().size() == Math.min(totalTargets, abTgt.getMaxTargets(sa.getHostCard(), sa)) - 1) {
- abTgt.addDividedAllocation(choice, left);
+ if (sa.getTargets().size() == Math.min(totalTargets, sa.getMaxTargets()) - 1) {
+ sa.addDividedAllocation(choice, left);
} else {
- abTgt.addDividedAllocation(choice, alloc);
+ sa.addDividedAllocation(choice, alloc);
left -= alloc;
}
}
diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java
index 35a2ba27f47..471cf90696f 100644
--- a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java
+++ b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java
@@ -268,7 +268,7 @@ public class DamageDealAi extends DamageAiBase {
if ((damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) ||
sourceName.equals("Crater's Claws")){
// 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;
final boolean noPrevention = sa.hasParam("NoPrevention");
for (final Card c : sa.getTargets().getTargetCards()) {
@@ -547,7 +547,7 @@ public class DamageDealAi extends DamageAiBase {
final boolean noPrevention = sa.hasParam("NoPrevention");
final Game game = source.getGame();
final PhaseHandler phase = game.getPhaseHandler();
- final boolean divided = sa.hasParam("DividedAsYouChoose");
+ final boolean divided = sa.isDividedAsYouChoose();
final boolean oppTargetsChoice = sa.hasParam("TargetingPlayer");
final String logic = sa.getParamOrDefault("AILogic", "");
@@ -613,7 +613,7 @@ public class DamageDealAi extends DamageAiBase {
if (assignedDamage <= dmg
&& humanCreature.getShieldCount() == 0 && !ComputerUtil.canRegenerate(humanCreature.getController(), humanCreature)) {
tcs.add(humanCreature);
- tgt.addDividedAllocation(humanCreature, assignedDamage);
+ sa.addDividedAllocation(humanCreature, assignedDamage);
lastTgt = humanCreature;
dmg -= assignedDamage;
}
@@ -625,7 +625,7 @@ public class DamageDealAi extends DamageAiBase {
}
}
if (dmg > 0 && lastTgt != null) {
- tgt.addDividedAllocation(lastTgt, tgt.getDividedValue(lastTgt) + dmg);
+ sa.addDividedAllocation(lastTgt, sa.getDividedValue(lastTgt) + dmg);
dmg = 0;
return true;
}
@@ -635,14 +635,14 @@ public class DamageDealAi extends DamageAiBase {
continue;
}
tcs.add(humanCreature);
- tgt.addDividedAllocation(humanCreature, dmg);
+ sa.addDividedAllocation(humanCreature, dmg);
dmg = 0;
return true;
}
}
int totalTargetedSoFar = -1;
- while (tcs.size() < tgt.getMaxTargets(source, sa)) {
+ while (sa.canAddMoreTarget()) {
if (totalTargetedSoFar == tcs.size()) {
// Avoid looping endlessly when choosing targets for cards with variable target number and type
// like Jaya's Immolating Inferno
@@ -664,7 +664,7 @@ public class DamageDealAi extends DamageAiBase {
if (divided) {
int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
assignedDamage = Math.min(dmg, assignedDamage);
- tgt.addDividedAllocation(c, assignedDamage);
+ sa.addDividedAllocation(c, assignedDamage);
dmg = dmg - assignedDamage;
if (dmg <= 0) {
break;
@@ -680,7 +680,7 @@ public class DamageDealAi extends DamageAiBase {
if (this.shouldTgtP(ai, sa, dmg, noPrevention)) {
tcs.add(enemy);
if (divided) {
- tgt.addDividedAllocation(enemy, dmg);
+ sa.addDividedAllocation(enemy, dmg);
break;
}
continue;
@@ -702,7 +702,7 @@ public class DamageDealAi extends DamageAiBase {
if (divided) {
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
if (assignedDamage <= dmg) {
- tgt.addDividedAllocation(c, assignedDamage);
+ sa.addDividedAllocation(c, assignedDamage);
}
dmg = dmg - assignedDamage;
if (dmg <= 0) {
@@ -739,7 +739,7 @@ public class DamageDealAi extends DamageAiBase {
if (freePing && sa.canTarget(enemy) && (!avoidTargetP(ai, sa))) {
tcs.add(enemy);
if (divided) {
- tgt.addDividedAllocation(enemy, dmg);
+ sa.addDividedAllocation(enemy, dmg);
break;
}
}
@@ -757,9 +757,9 @@ public class DamageDealAi extends DamageAiBase {
if (divided) {
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
if (assignedDamage <= dmg) {
- tgt.addDividedAllocation(c, assignedDamage);
+ sa.addDividedAllocation(c, assignedDamage);
} else {
- tgt.addDividedAllocation(c, dmg);
+ sa.addDividedAllocation(c, dmg);
}
dmg = dmg - assignedDamage;
if (dmg <= 0) {
@@ -785,7 +785,7 @@ public class DamageDealAi extends DamageAiBase {
(!avoidTargetP(ai, sa))) {
tcs.add(enemy);
if (divided) {
- tgt.addDividedAllocation(enemy, dmg);
+ sa.addDividedAllocation(enemy, dmg);
break;
}
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) {
// this is for Triggered targets that are mandatory
final boolean noPrevention = sa.hasParam("NoPrevention");
- final boolean divided = sa.hasParam("DividedAsYouChoose");
+ final boolean divided = sa.isDividedAsYouChoose();
final Player opp = ai.getWeakestOpponent();
- while (sa.getTargets().size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
+ while (sa.canAddMoreTarget()) {
if (tgt.canTgtPlaneswalker()) {
final Card c = this.dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, ai, true);
if (c != null) {
sa.getTargets().add(c);
if (divided) {
- tgt.addDividedAllocation(c, dmg);
+ sa.addDividedAllocation(c, dmg);
break;
}
continue;
@@ -902,7 +902,7 @@ public class DamageDealAi extends DamageAiBase {
if (c != null) {
sa.getTargets().add(c);
if (divided) {
- tgt.addDividedAllocation(c, dmg);
+ sa.addDividedAllocation(c, dmg);
break;
}
continue;
@@ -912,7 +912,7 @@ public class DamageDealAi extends DamageAiBase {
if (sa.canTarget(opp)) {
if (sa.getTargets().add(opp)) {
if (divided) {
- tgt.addDividedAllocation(opp, dmg);
+ sa.addDividedAllocation(opp, dmg);
break;
}
continue;
@@ -927,7 +927,7 @@ public class DamageDealAi extends DamageAiBase {
Card c = ComputerUtilCard.getWorstPermanentAI(indestructible, false, false, false, false);
sa.getTargets().add(c);
if (divided) {
- tgt.addDividedAllocation(c, dmg);
+ sa.addDividedAllocation(c, dmg);
break;
}
continue;
@@ -938,7 +938,7 @@ public class DamageDealAi extends DamageAiBase {
if (c != null) {
sa.getTargets().add(c);
if (divided) {
- tgt.addDividedAllocation(c, dmg);
+ sa.addDividedAllocation(c, dmg);
break;
}
continue;
@@ -948,7 +948,7 @@ public class DamageDealAi extends DamageAiBase {
if (sa.canTarget(ai)) {
if (sa.getTargets().add(ai)) {
if (divided) {
- tgt.addDividedAllocation(ai, dmg);
+ sa.addDividedAllocation(ai, dmg);
break;
}
continue;
@@ -988,7 +988,7 @@ public class DamageDealAi extends DamageAiBase {
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
int actualPay = 0;
final boolean noPrevention = sa.hasParam("NoPrevention");
diff --git a/forge-ai/src/main/java/forge/ai/ability/DamagePreventAi.java b/forge-ai/src/main/java/forge/ai/ability/DamagePreventAi.java
index 6e5259f25cb..964c427c649 100644
--- a/forge-ai/src/main/java/forge/ai/ability/DamagePreventAi.java
+++ b/forge-ai/src/main/java/forge/ai/ability/DamagePreventAi.java
@@ -146,8 +146,8 @@ public class DamagePreventAi extends SpellAbilityAi {
}
}
}
- if (tgt != null && sa.hasParam("DividedAsYouChoose") && sa.getTargets() != null && !sa.getTargets().isEmpty()) {
- tgt.addDividedAllocation(sa.getTargets().get(0), AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
+ if (sa.usesTargeting() && sa.isDividedAsYouChoose() && !sa.getTargets().isEmpty()) {
+ sa.addDividedAllocation(sa.getTargets().get(0), AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
}
return chance;
@@ -179,12 +179,11 @@ public class DamagePreventAi extends SpellAbilityAi {
* @return a boolean.
*/
private boolean preventDamageMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
- final TargetRestrictions tgt = sa.getTargetRestrictions();
sa.resetTargets();
// filter AIs battlefield by what I can target
final Game game = ai.getGame();
CardCollectionView targetables = game.getCardsIn(ZoneType.Battlefield);
- targetables = CardLists.getValidCards(targetables, tgt.getValidTgts(), ai, sa.getHostCard(), sa);
+ targetables = CardLists.getTargetableCards(targetables, sa);
final List compTargetables = CardLists.filterControlledBy(targetables, ai);
Card target = null;
@@ -215,8 +214,8 @@ public class DamagePreventAi extends SpellAbilityAi {
target = ComputerUtilCard.getCheapestPermanentAI(targetables, sa, true);
}
sa.getTargets().add(target);
- if (sa.hasParam("DividedAsYouChoose")) {
- tgt.addDividedAllocation(target, AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
+ if (sa.isDividedAsYouChoose()) {
+ sa.addDividedAllocation(target, AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa));
}
return true;
}
diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameSimulator.java b/forge-ai/src/main/java/forge/ai/simulation/GameSimulator.java
index 04308485a79..927b315bf00 100644
--- a/forge-ai/src/main/java/forge/ai/simulation/GameSimulator.java
+++ b/forge-ai/src/main/java/forge/ai/simulation/GameSimulator.java
@@ -16,7 +16,6 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetChoices;
-import forge.game.spellability.TargetRestrictions;
public class GameSimulator {
public static boolean COPY_STACK = false;
@@ -164,14 +163,12 @@ public class GameSimulator {
SpellAbility saOrSubSa = sa;
do {
if (origSaOrSubSa.usesTargeting()) {
- final boolean divided = origSaOrSubSa.hasParam("DividedAsYouChoose");
- final TargetRestrictions origTgtRes = origSaOrSubSa.getTargetRestrictions();
- final TargetRestrictions tgtRes = saOrSubSa.getTargetRestrictions();
+ final boolean divided = origSaOrSubSa.isDividedAsYouChoose();
for (final GameObject o : origSaOrSubSa.getTargets()) {
final GameObject target = copier.find(o);
saOrSubSa.getTargets().add(target);
if (divided) {
- tgtRes.addDividedAllocation(target, origTgtRes.getDividedValue(o));
+ saOrSubSa.addDividedAllocation(target, origSaOrSubSa.getDividedValue(o));
}
}
}
diff --git a/forge-ai/src/main/java/forge/ai/simulation/PossibleTargetSelector.java b/forge-ai/src/main/java/forge/ai/simulation/PossibleTargetSelector.java
index 164e564a15e..18e158e9a23 100644
--- a/forge-ai/src/main/java/forge/ai/simulation/PossibleTargetSelector.java
+++ b/forge-ai/src/main/java/forge/ai/simulation/PossibleTargetSelector.java
@@ -173,16 +173,15 @@ public class PossibleTargetSelector {
// Divide up counters, since AI is expected to do this. For now,
// divided evenly with left-overs going to the first target.
- if (targetingSa.hasParam("DividedAsYouChoose")) {
+ if (targetingSa.isDividedAsYouChoose()) {
final int targetCount = targetingSa.getTargets().getTargetCards().size();
if (targetCount > 0) {
final String amountStr = targetingSa.getParam("CounterNum");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, targetingSa);
final int amountPerCard = amount / targetCount;
int amountLeftOver = amount - (amountPerCard * targetCount);
- final TargetRestrictions tgtRes = targetingSa.getTargetRestrictions();
for (GameObject target : targetingSa.getTargets()) {
- tgtRes.addDividedAllocation(target, amountPerCard + amountLeftOver);
+ targetingSa.addDividedAllocation(target, amountPerCard + amountLeftOver);
amountLeftOver = 0;
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java
index b64e55edf59..752dfbbfdc7 100644
--- a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java
+++ b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java
@@ -364,10 +364,6 @@ public final class AbilityFactory {
if (mapParams.containsKey("TargetsWithDifferentCMC")) {
abTgt.setDifferentCMC(true);
}
- if (mapParams.containsKey("DividedAsYouChoose")) {
- abTgt.calculateStillToDivide(mapParams.get("DividedAsYouChoose"), null, null);
- abTgt.setDividedAsYouChoose(true);
- }
if (mapParams.containsKey("TargetsAtRandom")) {
abTgt.setRandomTarget(true);
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeTargetsEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeTargetsEffect.java
index 56477884311..c52942c35d5 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/ChangeTargetsEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeTargetsEffect.java
@@ -1,9 +1,11 @@
package forge.game.ability.effects;
+import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.game.GameEntity;
import forge.game.GameObject;
+import forge.game.GameObjectPredicates;
import forge.game.ability.SpellAbilityEffect;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -19,7 +21,7 @@ import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.List;
-/**
+/**
* TODO: Write javadoc for this type.
*
*/
@@ -42,9 +44,6 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
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;
Player chooser = sa.getActivatingPlayer();
@@ -54,11 +53,11 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
if (isOptional && !chooser.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantChangeAbilityTargets", tgtSA.getHostCard().toString()))) {
continue;
}
- if (changesOneTarget) {
+ if (sa.hasParam("ChangeSingleTarget")) {
// 1. choose a target of target spell
List> allTargets = new ArrayList<>();
while(changingTgtSI != null) {
- SpellAbility changedSa = changingTgtSI.getSpellAbility(true);
+ SpellAbility changedSa = changingTgtSI.getSpellAbility(true);
if (changedSa.usesTargeting()) {
for(GameObject it : changedSa.getTargets())
allTargets.add(ImmutablePair.of(changingTgtSI, it));
@@ -77,6 +76,8 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
GameObject oldTarget = chosenTarget.getValue();
TargetChoices oldTargetBlock = replaceIn.getTargetChoices();
TargetChoices newTargetBlock = oldTargetBlock.clone();
+ // gets the divied value from old target
+ Integer div = oldTargetBlock.getDividedValue(oldTarget);
newTargetBlock.remove(oldTarget);
replaceIn.updateTarget(newTargetBlock);
// 3. test if updated choices would be correct.
@@ -84,7 +85,10 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
if (replaceIn.getSpellAbility(true).canTarget(newTarget)) {
newTargetBlock.add(newTarget);
- replaceIn.updateTarget(newTargetBlock, oldTarget, newTarget);
+ if (div != null) {
+ newTargetBlock.addDividedAllocation(newTarget, div);
+ }
+ replaceIn.updateTarget(newTargetBlock);
}
else {
replaceIn.updateTarget(oldTargetBlock);
@@ -93,36 +97,38 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
else {
while(changingTgtSI != null) {
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();
List candidates = changingTgtSA.getTargetRestrictions().getAllCandidates(changingTgtSA, true);
GameEntity choice = Aggregates.random(candidates);
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")){
- GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
- if (newTarget != null && changingTgtSA.canTarget(newTarget)) {
- changingTgtSA.resetTargets();
- changingTgtSA.getTargets().add(newTarget);
- changingTgtSI.updateTarget(changingTgtSA.getTargets(), null, newTarget);
- }
- }
- else {
- // Update targets, with a potential new target
- TargetChoices newTarget = sa.getActivatingPlayer().getController().chooseNewTargetsFor(changingTgtSA);
- 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 if (sa.hasParam("DefinedMagnet")){
+ GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
+ if (newTarget != null && changingTgtSA.canTarget(newTarget)) {
+ int div = changingTgtSA.getTotalDividedValue();
+ changingTgtSA.resetTargets();
+ changingTgtSA.getTargets().add(newTarget);
+ changingTgtSI.updateTarget(changingTgtSA.getTargets());
+ if (changingTgtSA.isDividedAsYouChoose()) {
+ changingTgtSA.addDividedAllocation(newTarget, div);
}
- } else {
+ }
+ }
+ else {
+ // Update targets, with a potential new target
+ Predicate 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) {
changingTgtSI.updateTarget(newTarget);
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java
index 3c1ea43e9f3..50a18e79e60 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java
@@ -111,7 +111,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
candidates.remove(p);
for (GameEntity o : candidates) {
- SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
+ SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
resetFirstTargetOnCopy(copy, o, targetedSA);
copies.add(copy);
}
@@ -144,12 +144,12 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
}
for (final Card c : valid) {
- SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
+ SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
resetFirstTargetOnCopy(copy, c, targetedSA);
copies.add(copy);
}
for (final Player p : players) {
- SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
+ SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
resetFirstTargetOnCopy(copy, p, targetedSA);
copies.add(copy);
}
@@ -157,12 +157,9 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
}
else {
for (int i = 0; i < amount; i++) {
- SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA);
+ SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(sa, chosenSA, controller);
if (sa.hasParam("MayChooseTarget")) {
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
@@ -206,12 +203,9 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
}
int extraAmount = addAmount - copies.size();
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
copy.setMayChooseNewTargets(true);
- if (copy.usesTargeting()) {
- copy.getTargetRestrictions().setMandatory(true);
- }
copies.add(copy);
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java
index 9bef78eab96..598fc58446e 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java
@@ -27,13 +27,12 @@ public class CountersMoveEffect extends SpellAbilityEffect {
@Override
protected String getStackDescription(SpellAbility sa) {
- final Card host = sa.getHostCard();
final StringBuilder sb = new StringBuilder();
final List tgtCards = getDefinedCardsOrTargeted(sa);
Card source = null;
- if (sa.usesTargeting() && sa.getTargetRestrictions().getMinTargets(host, sa) == 2) {
+ if (sa.usesTargeting() && sa.getMinTargets() == 2) {
if (tgtCards.size() < 2) {
return "";
}
@@ -241,7 +240,7 @@ public class CountersMoveEffect extends SpellAbilityEffect {
Card source = null;
List tgtCards = getDefinedCardsOrTargeted(sa);
// 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) {
return;
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java
index d3ad905eaf2..1dad1191eef 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java
@@ -46,7 +46,6 @@ public class CountersPutEffect extends SpellAbilityEffect {
protected String getStackDescription(SpellAbility spellAbility) {
final StringBuilder stringBuilder = new StringBuilder();
final Card card = spellAbility.getHostCard();
- final boolean dividedAsYouChoose = spellAbility.hasParam("DividedAsYouChoose");
final int amount = AbilityUtils.calculateAmount(card, spellAbility.getParamOrDefault("CounterNum", "1"), spellAbility);
//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);
return stringBuilder.toString();
}
- if (dividedAsYouChoose) {
+ if (spellAbility.isDividedAsYouChoose()) {
stringBuilder.append("Distribute ");
} else {
stringBuilder.append("Put ");
@@ -84,7 +83,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
stringBuilder.append("s");
}
- if (dividedAsYouChoose) {
+ if (spellAbility.isDividedAsYouChoose()) {
stringBuilder.append(" among ");
} else {
stringBuilder.append(" on ");
@@ -96,8 +95,9 @@ public class CountersPutEffect extends SpellAbilityEffect {
for(int i = 0; i < targetCards.size(); i++) {
Card targetCard = targetCards.get(i);
stringBuilder.append(targetCard);
- if (spellAbility.getTargetRestrictions().getDividedMap().get(targetCard) != null) // fix null counter stack description
- stringBuilder.append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetCard)).append(" counter)");
+ Integer v = spellAbility.getDividedValue(targetCard);
+ if (v != null) // fix null counter stack description
+ stringBuilder.append(" (").append(v).append(" counter)");
if(i == targetCards.size() - 2) {
stringBuilder.append(" and ");
@@ -259,7 +259,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
if (obj instanceof Card) {
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 (max != -1) {
counterAmount = Math.max(Math.min(max - gameCard.getCounters(counterType), counterAmount), 0);
@@ -270,7 +270,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
params.put("CounterType", counterType);
counterAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyCounters"), 0, counterAmount, params);
}
- if (sa.hasParam("DividedAsYouChoose") && !sa.usesTargeting()) {
+ if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) {
Map params = Maps.newHashMap();
params.put("Target", obj);
params.put("CounterType", counterType);
@@ -378,7 +378,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
card.addRemembered(gameCard);
}
game.updateLastStateForCard(gameCard);
- if (sa.hasParam("DividedAsYouChoose") && !sa.usesTargeting()) {
+ if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) {
counterRemain = counterRemain - counterAmount;
}
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java
index 5f0b4608c55..5b9e0542f5e 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java
@@ -61,7 +61,7 @@ public class DamageDealEffect extends DamageBaseEffect {
if (spellAbility.usesTargeting()) {
if (spellAbility.hasParam("DivideEvenly")) {
stringBuilder.append("divided evenly (rounded down) to\n");
- } else if (spellAbility.hasParam("DividedAsYouChoose")) {
+ } else if (spellAbility.isDividedAsYouChoose()) {
stringBuilder.append("divided to\n");
} else
stringBuilder.append("to ");
@@ -75,8 +75,9 @@ public class DamageDealEffect extends DamageBaseEffect {
for (int i = 0; i < targetCards.size(); i++) {
Card targetCard = targetCards.get(i);
stringBuilder.append(targetCard);
- if (spellAbility.getTargetRestrictions().getDividedMap().get(targetCard) != null) //fix null damage stack description
- stringBuilder.append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetCard)).append(" damage)");
+ Integer v = spellAbility.getDividedValue(targetCard);
+ if (v != null) //fix null damage stack description
+ stringBuilder.append(" (").append(v).append(" damage)");
if (i == targetCount - 2) {
stringBuilder.append(" and ");
@@ -89,8 +90,9 @@ public class DamageDealEffect extends DamageBaseEffect {
for (int i = 0; i < players.size(); i++) {
Player targetPlayer = players.get(i);
stringBuilder.append(targetPlayer);
- if (spellAbility.getTargetRestrictions().getDividedMap().get(targetPlayer) != null) //fix null damage stack description
- stringBuilder.append(" (").append(spellAbility.getTargetRestrictions().getDividedMap().get(targetPlayer)).append(" damage)");
+ Integer v = spellAbility.getDividedValue(targetPlayer);
+ if (v != null) //fix null damage stack description
+ stringBuilder.append(" (").append(v).append(" damage)");
if (i == players.size() - 2) {
stringBuilder.append(" and ");
@@ -102,7 +104,7 @@ public class DamageDealEffect extends DamageBaseEffect {
} else {
if (spellAbility.hasParam("DivideEvenly")) {
stringBuilder.append("divided evenly (rounded down) ");
- } else if (spellAbility.hasParam("DividedAsYouChoose")) {
+ } else if (spellAbility.isDividedAsYouChoose()) {
stringBuilder.append("divided as you choose ");
}
stringBuilder.append("to ").append(Lang.joinHomogenous(targets));
@@ -229,7 +231,7 @@ public class DamageDealEffect extends DamageBaseEffect {
for (final GameObject o : tgts) {
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) {
continue;
}
diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java
index 710f2f747b6..54e0395a34e 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java
@@ -25,7 +25,7 @@ public class DamagePreventEffect extends SpellAbilityEffect {
sb.append("Prevent the next ");
sb.append(sa.getParam("Amount"));
sb.append(" damage that would be dealt ");
- if (sa.hasParam("DividedAsYouChoose")) {
+ if (sa.isDividedAsYouChoose()) {
sb.append("between ");
} else {
sb.append("to ");
@@ -75,8 +75,8 @@ public class DamagePreventEffect extends SpellAbilityEffect {
final boolean targeted = (sa.usesTargeting());
final boolean preventionWithEffect = sa.hasParam("PreventionSubAbility");
- for (final Object o : tgts) {
- numDam = (sa.usesTargeting() && sa.hasParam("DividedAsYouChoose")) ? sa.getTargetRestrictions().getDividedValue(o) : numDam;
+ for (final GameObject o : tgts) {
+ numDam = (sa.usesTargeting() && sa.isDividedAsYouChoose()) ? sa.getDividedValue(o) : numDam;
if (o instanceof Card) {
final Card c = (Card) o;
if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) {
diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java
index 17490640e78..d6f59f35b1c 100644
--- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java
@@ -260,13 +260,8 @@ public class PlayEffect extends SpellAbilityEffect {
continue;
}
- final boolean noManaCost = sa.hasParam("WithoutManaCost");
- if (noManaCost) {
+ if (sa.hasParam("WithoutManaCost")) {
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")) {
Cost abCost;
if ("ManaCost".equals(sa.getParam("PlayCost"))) {
diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java
index dce30d6d5b7..bbcc57a8d4b 100644
--- a/forge-game/src/main/java/forge/game/card/CardFactory.java
+++ b/forge-game/src/main/java/forge/game/card/CardFactory.java
@@ -117,10 +117,9 @@ public class CardFactory {
* which wouldn't ordinarily get set during a simple Card.copy() call.
*
* */
- 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 original = targetSA.getHostCard();
- Player controller = sourceSA.getActivatingPlayer();
final Card c = copyCard(original, true);
// change the color of the copy (eg: Fork)
@@ -168,17 +167,15 @@ public class CardFactory {
* @param bCopyDetails
* a boolean.
*/
- public final static SpellAbility copySpellAbilityAndPossiblyHost(final SpellAbility sourceSA, final SpellAbility targetSA) {
- Player controller = sourceSA.getActivatingPlayer();
-
+ public final static SpellAbility copySpellAbilityAndPossiblyHost(final SpellAbility sourceSA, final SpellAbility targetSA, final Player controller) {
//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;
if (targetSA.isTrigger() && targetSA.isWrapper()) {
- copySA = getCopiedTriggeredAbility((WrappedAbility)targetSA, c);
+ copySA = getCopiedTriggeredAbility((WrappedAbility)targetSA, c, controller);
} else {
- copySA = targetSA.copy(c, false);
+ copySA = targetSA.copy(c, controller, false);
}
copySA.setCopied(true);
@@ -555,12 +552,12 @@ public class CardFactory {
*
* 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()) {
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) {
diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java
index bcada42ecd5..b09df0a2b7b 100644
--- a/forge-game/src/main/java/forge/game/player/PlayerController.java
+++ b/forge-game/src/main/java/forge/game/player/PlayerController.java
@@ -112,7 +112,7 @@ public abstract class PlayerController {
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 choosePermanentsToDestroy(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
- public abstract TargetChoices chooseNewTargetsFor(SpellAbility ability);
+ public abstract TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate filter, boolean optional);
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)
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
index 11fef9d1084..ac1ef541453 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java
@@ -152,6 +152,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private TargetRestrictions targetRestrictions = null;
private TargetChoices targetChosen = new TargetChoices();
+ private Integer dividedValue = null;
+
private SpellAbilityView view;
private StaticAbility mayPlay = null;
@@ -1101,7 +1103,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
if (hasParam("TargetingPlayerControls") && entity instanceof Card) {
final Card c = (Card) entity;
- if (!c.getController().equals(targetingPlayer)) {
+ if (!c.getController().equals(getTargetingPlayer())) {
return false;
}
}
@@ -1417,6 +1419,35 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
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.
*
@@ -1424,16 +1455,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public void resetFirstTarget(GameObject c, SpellAbility originalSA) {
SpellAbility sa = this;
while (sa != null) {
- if (sa.targetRestrictions != null) {
- sa.targetChosen = new TargetChoices();
- sa.targetChosen.add(c);
- if (!originalSA.targetRestrictions.getDividedMap().isEmpty()) {
- sa.targetRestrictions.addDividedAllocation(c,
- Iterables.getFirst(originalSA.targetRestrictions.getDividedMap().values(), null));
+ if (sa.usesTargeting()) {
+ sa.resetTargets();
+ sa.getTargets().add(c);
+ if (!originalSA.getTargets().getDividedValues().isEmpty()) {
+ sa.addDividedAllocation(c, Iterables.getFirst(originalSA.getTargets().getDividedValues(), null));
}
break;
}
- sa = sa.subAbility;
+ sa = sa.getSubAbility();
}
}
@@ -1450,7 +1480,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
public boolean isZeroTargets() {
- return getTargetRestrictions().getMinTargets(hostCard, this) == 0 && getTargets().size() == 0;
+ return getMinTargets() == 0 && getTargets().size() == 0;
}
public boolean isMinTargetChosen() {
@@ -1724,6 +1754,27 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
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() {
// Skip to paying if parent ability doesn't target and has no subAbilities.
// (or trigger case where its already targeted)
@@ -1741,6 +1792,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
} else {
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);
if (!targetingPlayer.getController().chooseTargetsFor(currentAbility)) {
return false;
@@ -1756,11 +1809,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return true;
}
public final void clearTargets() {
- final TargetRestrictions tg = getTargetRestrictions();
- if (tg != null) {
+ if (usesTargeting()) {
resetTargets();
- if (hasParam("DividedAsYouChoose")) {
- tg.calculateStillToDivide(getParam("DividedAsYouChoose"), getHostCard(), this);
+ if (isDividedAsYouChoose()) {
+ this.dividedValue = AbilityUtils.calculateAmount(getHostCard(), this.getParam("DividedAsYouChoose"), this);
}
}
}
diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java
index ee737cb3b05..14106f5c977 100644
--- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java
+++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java
@@ -32,11 +32,10 @@ import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
import forge.game.GameObject;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -85,7 +84,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
private Integer xManaPaid = null;
// Other Paid things
- private final HashMap paidHash;
+ private final Map paidHash;
// Additional info
// is Kicked, is Buyback
@@ -96,7 +95,6 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
private final Map storedSVars = Maps.newHashMap();
- private final List zonesToOpen;
private final Map playersWithValidTargets;
private final StackItemView view;
@@ -109,7 +107,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
activatingPlayer = sa.getActivatingPlayer();
// Payment info
- paidHash = new HashMap<>(ability.getPaidHash());
+ paidHash = Maps.newHashMap(ability.getPaidHash());
ability.resetPaidHash();
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
if (tc == null) {
- zonesToOpen = null;
playersWithValidTargets = null;
}
else {
- zonesToOpen = new ArrayList<>();
- playersWithValidTargets = new HashMap<>();
+ playersWithValidTargets = Maps.newHashMap();
for (Card card : tc.getTargetCards()) {
ZoneType zoneType = card.getZone() != null ? card.getZone().getZoneType() : null;
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);
}
}
@@ -253,75 +246,32 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
return tc;
}
- public final List getZonesToOpen() {
- return zonesToOpen;
- }
-
public final Map getPlayersWithValidTargets() {
return playersWithValidTargets;
}
public void updateTarget(TargetChoices target) {
- updateTarget(target, null, null);
- }
-
- public void updateTarget(TargetChoices target, GameObject oldTarget, GameObject newTarget) {
if (target != null) {
+ TargetChoices oldTarget = tc;
tc = target;
ability.setTargets(tc);
stackDescription = ability.getStackDescription();
view.updateTargetCards(this);
view.updateTargetPlayers(this);
view.updateText(this);
-
- if (ability.hasParam("DividedAsYouChoose")) {
- // try to update DividedAsYouChoose after retargeting
- Object toRemove = null;
- Object toAdd = null;
- HashMap