- 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

2
.gitattributes vendored
View File

@@ -10592,6 +10592,7 @@ res/cardsfolder/s/sway_of_illusion.txt -text
res/cardsfolder/s/sway_of_the_stars.txt svneol=native#text/plain res/cardsfolder/s/sway_of_the_stars.txt svneol=native#text/plain
res/cardsfolder/s/swell_of_courage.txt svneol=native#text/plain res/cardsfolder/s/swell_of_courage.txt svneol=native#text/plain
res/cardsfolder/s/swelter.txt svneol=native#text/plain res/cardsfolder/s/swelter.txt svneol=native#text/plain
res/cardsfolder/s/swerve.txt -text
res/cardsfolder/s/swift_justice.txt -text res/cardsfolder/s/swift_justice.txt -text
res/cardsfolder/s/swift_maneuver.txt svneol=native#text/plain res/cardsfolder/s/swift_maneuver.txt svneol=native#text/plain
res/cardsfolder/s/swift_silence.txt -text res/cardsfolder/s/swift_silence.txt -text
@@ -13628,6 +13629,7 @@ src/main/java/forge/card/ability/effects/AnimateEffectBase.java svneol=native#te
src/main/java/forge/card/ability/effects/AttachEffect.java -text src/main/java/forge/card/ability/effects/AttachEffect.java -text
src/main/java/forge/card/ability/effects/BecomesBlockedEffect.java -text src/main/java/forge/card/ability/effects/BecomesBlockedEffect.java -text
src/main/java/forge/card/ability/effects/BondEffect.java -text src/main/java/forge/card/ability/effects/BondEffect.java -text
src/main/java/forge/card/ability/effects/ChangeTargetsEffect.java -text
src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java -text src/main/java/forge/card/ability/effects/ChangeZoneAllEffect.java -text
src/main/java/forge/card/ability/effects/ChangeZoneEffect.java -text src/main/java/forge/card/ability/effects/ChangeZoneEffect.java -text
src/main/java/forge/card/ability/effects/CharmEffect.java -text src/main/java/forge/card/ability/effects/CharmEffect.java -text

View File

@@ -0,0 +1,6 @@
Name:Swerve
ManaCost:U R
Types:Instant
A:SP$ ChangeTargets | Cost$ U R | TargetType$ Spell | ValidTgts$ Card | TargetsSingleTarget$ True | SpellDescription$ Change the target of target spell with a single target.
Oracle:Change the target of target spell with a single target.
SetInfo:ALA Uncommon

View File

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

View File

@@ -115,6 +115,7 @@ public enum ApiType {
Attach (AttachEffect.class, AttachAi.class), Attach (AttachEffect.class, AttachAi.class),
BecomesBlocked (BecomesBlockedEffect.class, BecomesBlockedAi.class), BecomesBlocked (BecomesBlockedEffect.class, BecomesBlockedAi.class),
Bond (BondEffect.class, BondAi.class), Bond (BondEffect.class, BondAi.class),
ChangeTargets(ChangeTargetsEffect.class, CannotPlayAi.class),
ChangeZone(ChangeZoneEffect.class, ChangeZoneAi.class), ChangeZone(ChangeZoneEffect.class, ChangeZoneAi.class),
ChangeZoneAll(ChangeZoneAllEffect.class, ChangeZoneAllAi.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.*/ /** 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()); return topSA.getSourceCard().isValid(tgt.getValidTgts(), this.getActivatingPlayer(), this.getSourceCard());
} }

View File

@@ -256,4 +256,12 @@ public class SpellAbilityStackInstance {
public final boolean isOptionalTrigger() { public final boolean isOptionalTrigger() {
return this.ability.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 differentControllers = false;
private boolean sameController = false; private boolean sameController = false;
private boolean withoutSameCreatureType = false; private boolean withoutSameCreatureType = false;
private boolean singleTarget = false;
private String definedController = null; private String definedController = null;
// How many can be targeted? // How many can be targeted?
@@ -105,6 +106,8 @@ public class Target {
this.sameController = target.isSameController(); this.sameController = target.isSameController();
this.withoutSameCreatureType = target.isWithoutSameCreatureType(); this.withoutSameCreatureType = target.isWithoutSameCreatureType();
this.definedController = target.getDefinedController(); 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)) { if (this.tgtZone.contains(ZoneType.Stack)) {
// Stack Zone targets are considered later
return true; return true;
} else { } else {
for (final Card c : Singletons.getModel().getGame().getCardsIn(this.tgtZone)) { for (final Card c : Singletons.getModel().getGame().getCardsIn(this.tgtZone)) {
@@ -850,6 +854,20 @@ public class Target {
this.definedController = defined; 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 * @return a boolean dividedAsYouChoose
*/ */

View File

@@ -34,6 +34,11 @@ import forge.game.player.Player;
public class TargetChoices { public class TargetChoices {
private int numTargeted = 0; 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> * <p>
* Getter for the field <code>numTargeted</code>. * Getter for the field <code>numTargeted</code>.
@@ -45,11 +50,6 @@ public class TargetChoices {
return this.numTargeted; 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> * <p>
* addTarget. * addTarget.

View File

@@ -9,6 +9,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card; import forge.Card;
import forge.GameEntity; import forge.GameEntity;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.control.input.Input; import forge.control.input.Input;
import forge.deck.Deck; import forge.deck.Deck;
import forge.game.GameState; import forge.game.GameState;
@@ -96,6 +97,7 @@ public abstract class PlayerController {
public abstract Integer announceRequirements(SpellAbility ability, String announce, boolean allowZero); 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 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 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); 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.GameEntity;
import forge.card.spellability.Spell; import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.control.input.Input; import forge.control.input.Input;
import forge.control.input.InputAutoPassPriority; import forge.control.input.InputAutoPassPriority;
import forge.deck.Deck; import forge.deck.Deck;
@@ -252,4 +253,13 @@ public class PlayerControllerAi extends PlayerController {
return getAi().chooseCardsToDelve(colorlessCost, grave); 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.FThreads;
import forge.GameEntity; import forge.GameEntity;
import forge.card.spellability.SpellAbility; 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.Input;
import forge.control.input.InputAutoPassPriority; import forge.control.input.InputAutoPassPriority;
import forge.control.input.InputBlock; import forge.control.input.InputBlock;
@@ -401,4 +404,20 @@ public class PlayerControllerHuman extends PlayerController {
} }
return toExile; 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;
}
}
} }