mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
- Added ChangeTargets Ability Effect
- Added Swerve as ChangeTargets example of both ChangeTargets and singleTarget
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -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
|
||||||
|
|||||||
6
res/cardsfolder/s/swerve.txt
Normal file
6
res/cardsfolder/s/swerve.txt
Normal 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
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.*/
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user