mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
- Added ChangeTargets Ability Effect
- Added Swerve as ChangeTargets example of both ChangeTargets and singleTarget
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.*/
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user