- Added ChangeTargets Ability Effect

- Added Swerve as ChangeTargets example of both ChangeTargets and singleTarget
This commit is contained in:
Sol
2013-04-05 00:33:28 +00:00
parent 4ada471f69
commit b78dfa7cea
12 changed files with 132 additions and 8 deletions

View File

@@ -108,9 +108,9 @@ public final class AbilityFactory {
hostCard.setCopiesSpells(true);
}
else if (api == ApiType.Counter) {
// Since all "Counter" ABs Counter things on the Stack no need for
// it to be everywhere
else if (api == ApiType.Counter || api == ApiType.ChangeTargets) {
// Since all "Counter" or "ChangeTargets" abilities only target the Stack Zone
// No need to have each of those scripts have that info
if (abTgt != null) {
abTgt.setZone(ZoneType.Stack);
}
@@ -227,6 +227,9 @@ public final class AbilityFactory {
abTgt.setSAValidTargeting(mapParams.get("TargetValidTargeting"));
}
if (mapParams.containsKey("TargetsSingleTarget")) {
abTgt.setSingleTarget(true);
}
if (mapParams.containsKey("TargetUnique")) {
abTgt.setUniqueTargets(true);
}

View File

@@ -115,6 +115,7 @@ public enum ApiType {
Attach (AttachEffect.class, AttachAi.class),
BecomesBlocked (BecomesBlockedEffect.class, BecomesBlockedAi.class),
Bond (BondEffect.class, BondAi.class),
ChangeTargets(ChangeTargetsEffect.class, CannotPlayAi.class),
ChangeZone(ChangeZoneEffect.class, ChangeZoneAi.class),
ChangeZoneAll(ChangeZoneAllEffect.class, ChangeZoneAllAi.class),
/** This is <b>Modal</b>, like 'choose one - ' or 'choose two - '. <br> Might be great to rename this api and update all scripts.*/

View File

@@ -0,0 +1,39 @@
package forge.card.ability.effects;
import java.util.List;
import forge.Card;
import forge.Singletons;
import forge.card.ability.SpellAbilityEffect;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.spellability.Target;
import forge.card.spellability.TargetSelection;
import forge.game.zone.MagicStack;
/**
* TODO: Write javadoc for this type.
*
*/
public class ChangeTargetsEffect extends SpellAbilityEffect {
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityEffect#resolve(forge.card.spellability.SpellAbility)
*/
@Override
public void resolve(SpellAbility sa) {
final List<SpellAbility> sas = getTargetSpellAbilities(sa);
final MagicStack stack = sa.getActivatingPlayer().getGame().getStack();
for (final SpellAbility tgtSA : sas) {
SpellAbilityStackInstance si = stack.getInstanceFromSpellAbility(tgtSA);
if (si == null) {
// If there isn't a Stack Instance, there isn't really a target
continue;
}
// Update targets, with a potential new target
si.updateTarget(sa.getActivatingPlayer().getController().chooseTargets(tgtSA));
}
}
}

View File

@@ -1688,6 +1688,22 @@ public abstract class SpellAbility implements ISpellAbility {
}
}
if (tgt.isSingleTarget()) {
final ArrayList<TargetChoices> choices = topSA.getAllTargetChoices();
int totalTargets = 0;
for(TargetChoices tc : choices) {
totalTargets += tc.getNumTargeted();
if (totalTargets > 1) {
// As soon as we get more than one, bail out
return false;
}
}
if (totalTargets != 1) {
// Make sure that there actually is one target
return false;
}
}
return topSA.getSourceCard().isValid(tgt.getValidTgts(), this.getActivatingPlayer(), this.getSourceCard());
}

View File

@@ -256,4 +256,12 @@ public class SpellAbilityStackInstance {
public final boolean isOptionalTrigger() {
return this.ability.isOptionalTrigger();
}
public void updateTarget(Target target) {
if (target != null) {
this.tc = target.getTargetChoices();
this.ability.setTarget(target);
this.stackDescription = this.ability.getStackDescription();
}
}
}

View File

@@ -62,6 +62,7 @@ public class Target {
private boolean differentControllers = false;
private boolean sameController = false;
private boolean withoutSameCreatureType = false;
private boolean singleTarget = false;
private String definedController = null;
// How many can be targeted?
@@ -105,6 +106,8 @@ public class Target {
this.sameController = target.isSameController();
this.withoutSameCreatureType = target.isWithoutSameCreatureType();
this.definedController = target.getDefinedController();
this.singleTarget = target.isSingleTarget();
this.choice = target.getTargetChoices();
}
/**
@@ -663,6 +666,7 @@ public class Target {
}
if (this.tgtZone.contains(ZoneType.Stack)) {
// Stack Zone targets are considered later
return true;
} else {
for (final Card c : Singletons.getModel().getGame().getCardsIn(this.tgtZone)) {
@@ -850,6 +854,20 @@ public class Target {
this.definedController = defined;
}
/**
* @return the singleTarget
*/
public boolean isSingleTarget() {
return singleTarget;
}
/**
* @param singleTarget the singleTarget to set
*/
public void setSingleTarget(boolean singleTarget) {
this.singleTarget = singleTarget;
}
/**
* @return a boolean dividedAsYouChoose
*/

View File

@@ -34,6 +34,11 @@ import forge.game.player.Player;
public class TargetChoices {
private int numTargeted = 0;
// Card or Player are legal targets.
private final List<Card> targetCards = new ArrayList<Card>();
private final List<Player> targetPlayers = new ArrayList<Player>();
private final List<SpellAbility> targetSAs = new ArrayList<SpellAbility>();
/**
* <p>
* Getter for the field <code>numTargeted</code>.
@@ -45,11 +50,6 @@ public class TargetChoices {
return this.numTargeted;
}
// Card or Player are legal targets.
private final List<Card> targetCards = new ArrayList<Card>();
private final List<Player> targetPlayers = new ArrayList<Player>();
private final List<SpellAbility> targetSAs = new ArrayList<SpellAbility>();
/**
* <p>
* addTarget.

View File

@@ -9,6 +9,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card;
import forge.GameEntity;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.control.input.Input;
import forge.deck.Deck;
import forge.game.GameState;
@@ -96,6 +97,7 @@ public abstract class PlayerController {
public abstract Integer announceRequirements(SpellAbility ability, String announce, boolean allowZero);
public abstract List<Card> choosePermanentsToSacrifice(List<Card> validTargets, String validMessage, int amount, SpellAbility sa, boolean destroy, boolean isOptional);
public abstract Target chooseTargets(SpellAbility ability);
public Card chooseSingleCardForEffect(List<Card> sourceList, SpellAbility sa, String title) { return chooseSingleCardForEffect(sourceList, sa, title, false); }
public abstract Card chooseSingleCardForEffect(List<Card> sourceList, SpellAbility sa, String title, boolean isOptional);

View File

@@ -12,6 +12,7 @@ import forge.Card;
import forge.GameEntity;
import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.control.input.Input;
import forge.control.input.InputAutoPassPriority;
import forge.deck.Deck;
@@ -252,4 +253,13 @@ public class PlayerControllerAi extends PlayerController {
return getAi().chooseCardsToDelve(colorlessCost, grave);
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseTargets(forge.card.spellability.SpellAbility, forge.card.spellability.SpellAbilityStackInstance)
*/
@Override
public Target chooseTargets(SpellAbility ability) {
// AI currently can't do this. But when it can it will need to be based on Ability API
return null;
}
}

View File

@@ -15,6 +15,9 @@ import forge.Card;
import forge.FThreads;
import forge.GameEntity;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.card.spellability.TargetChoices;
import forge.card.spellability.TargetSelection;
import forge.control.input.Input;
import forge.control.input.InputAutoPassPriority;
import forge.control.input.InputBlock;
@@ -401,4 +404,20 @@ public class PlayerControllerHuman extends PlayerController {
}
return toExile;
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseTargets(forge.card.spellability.SpellAbility, forge.card.spellability.SpellAbilityStackInstance)
*/
@Override
public Target chooseTargets(SpellAbility ability) {
Target oldTarget = new Target(ability.getTarget());
TargetSelection select = new TargetSelection(ability);
ability.getTarget().resetTargets();
if (select.chooseTargets()) {
return ability.getTarget();
} else {
// Return old target, since we had to reset them above
return oldTarget;
}
}
}