From b78dfa7cea2dfe091eb51ca3ca0a10ae4f7fdfe9 Mon Sep 17 00:00:00 2001 From: Sol Date: Fri, 5 Apr 2013 00:33:28 +0000 Subject: [PATCH] - Added ChangeTargets Ability Effect - Added Swerve as ChangeTargets example of both ChangeTargets and singleTarget --- .gitattributes | 2 + res/cardsfolder/s/swerve.txt | 6 +++ .../forge/card/ability/AbilityFactory.java | 9 +++-- src/main/java/forge/card/ability/ApiType.java | 1 + .../ability/effects/ChangeTargetsEffect.java | 39 +++++++++++++++++++ .../forge/card/spellability/SpellAbility.java | 16 ++++++++ .../SpellAbilityStackInstance.java | 8 ++++ .../java/forge/card/spellability/Target.java | 18 +++++++++ .../card/spellability/TargetChoices.java | 10 ++--- .../forge/game/player/PlayerController.java | 2 + .../forge/game/player/PlayerControllerAi.java | 10 +++++ .../game/player/PlayerControllerHuman.java | 19 +++++++++ 12 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 res/cardsfolder/s/swerve.txt create mode 100644 src/main/java/forge/card/ability/effects/ChangeTargetsEffect.java diff --git a/.gitattributes b/.gitattributes index 842b74895ee..9195d644f37 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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/swell_of_courage.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_maneuver.txt svneol=native#text/plain 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/BecomesBlockedEffect.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/ChangeZoneEffect.java -text src/main/java/forge/card/ability/effects/CharmEffect.java -text diff --git a/res/cardsfolder/s/swerve.txt b/res/cardsfolder/s/swerve.txt new file mode 100644 index 00000000000..b5381b976cd --- /dev/null +++ b/res/cardsfolder/s/swerve.txt @@ -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 \ No newline at end of file diff --git a/src/main/java/forge/card/ability/AbilityFactory.java b/src/main/java/forge/card/ability/AbilityFactory.java index 6a782de467f..1ba18db3f88 100644 --- a/src/main/java/forge/card/ability/AbilityFactory.java +++ b/src/main/java/forge/card/ability/AbilityFactory.java @@ -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); } diff --git a/src/main/java/forge/card/ability/ApiType.java b/src/main/java/forge/card/ability/ApiType.java index ae794f7c796..cd251447825 100644 --- a/src/main/java/forge/card/ability/ApiType.java +++ b/src/main/java/forge/card/ability/ApiType.java @@ -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 Modal, like 'choose one - ' or 'choose two - '.
Might be great to rename this api and update all scripts.*/ diff --git a/src/main/java/forge/card/ability/effects/ChangeTargetsEffect.java b/src/main/java/forge/card/ability/effects/ChangeTargetsEffect.java new file mode 100644 index 00000000000..c821ed0c3ae --- /dev/null +++ b/src/main/java/forge/card/ability/effects/ChangeTargetsEffect.java @@ -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 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)); + } + } +} diff --git a/src/main/java/forge/card/spellability/SpellAbility.java b/src/main/java/forge/card/spellability/SpellAbility.java index 6e653839a50..998810f105b 100644 --- a/src/main/java/forge/card/spellability/SpellAbility.java +++ b/src/main/java/forge/card/spellability/SpellAbility.java @@ -1688,6 +1688,22 @@ public abstract class SpellAbility implements ISpellAbility { } } + if (tgt.isSingleTarget()) { + final ArrayList 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()); } diff --git a/src/main/java/forge/card/spellability/SpellAbilityStackInstance.java b/src/main/java/forge/card/spellability/SpellAbilityStackInstance.java index c5afabcd99d..b80e183f71f 100644 --- a/src/main/java/forge/card/spellability/SpellAbilityStackInstance.java +++ b/src/main/java/forge/card/spellability/SpellAbilityStackInstance.java @@ -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(); + } + } } diff --git a/src/main/java/forge/card/spellability/Target.java b/src/main/java/forge/card/spellability/Target.java index 87c6cdd80d7..8f31ef64ba3 100644 --- a/src/main/java/forge/card/spellability/Target.java +++ b/src/main/java/forge/card/spellability/Target.java @@ -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 */ diff --git a/src/main/java/forge/card/spellability/TargetChoices.java b/src/main/java/forge/card/spellability/TargetChoices.java index 62e5f7ba9a3..ae81a496d63 100644 --- a/src/main/java/forge/card/spellability/TargetChoices.java +++ b/src/main/java/forge/card/spellability/TargetChoices.java @@ -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 targetCards = new ArrayList(); + private final List targetPlayers = new ArrayList(); + private final List targetSAs = new ArrayList(); + /** *

* Getter for the field numTargeted. @@ -45,11 +50,6 @@ public class TargetChoices { return this.numTargeted; } - // Card or Player are legal targets. - private final List targetCards = new ArrayList(); - private final List targetPlayers = new ArrayList(); - private final List targetSAs = new ArrayList(); - /** *

* addTarget. diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index 993699bda94..a6fa341be04 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -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 choosePermanentsToSacrifice(List validTargets, String validMessage, int amount, SpellAbility sa, boolean destroy, boolean isOptional); + public abstract Target chooseTargets(SpellAbility ability); public Card chooseSingleCardForEffect(List sourceList, SpellAbility sa, String title) { return chooseSingleCardForEffect(sourceList, sa, title, false); } public abstract Card chooseSingleCardForEffect(List sourceList, SpellAbility sa, String title, boolean isOptional); diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 47806016f05..e1866e3a45b 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -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; + } + } diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 39081958b49..62bb4a4c0b0 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -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; + } + } }