spellskite added - please test

This commit is contained in:
Maxmtg
2013-06-21 04:16:59 +00:00
parent 775588b300
commit 00ddfed039
13 changed files with 156 additions and 24 deletions

1
.gitattributes vendored
View File

@@ -10366,6 +10366,7 @@ res/cardsfolder/s/spellgorger_barbarian.txt svneol=native#text/plain
res/cardsfolder/s/spelljack.txt -text
res/cardsfolder/s/spellshift.txt -text
res/cardsfolder/s/spellshock.txt svneol=native#text/plain
res/cardsfolder/s/spellskite.txt -text
res/cardsfolder/s/spellstutter_sprite.txt svneol=native#text/plain
res/cardsfolder/s/spelltithe_enforcer.txt -text
res/cardsfolder/s/spelltwine.txt -text

View File

@@ -0,0 +1,7 @@
Name:Spellskite
ManaCost:2
Types:Artifact Creature Horror
PT:0/4
A:AB$ ChangeTargets | Cost$ PU | TargetType$ Spell | ValidTgts$ Card | SpellSkite$ True | SpellDescription$ Change a target of target spell or ability to Spellskite.
SVar:Picture:http://www.wizards.com/global/images/magic/general/spellskite.jpg
Oracle:{UP}: Change a target of target spell or ability to Spellskite. ({UP} can be paid with either {U} or 2 life.)

View File

@@ -1,11 +1,18 @@
package forge.card.ability.effects;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import forge.Card;
import forge.ITargetable;
import forge.card.ability.SpellAbilityEffect;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.spellability.TargetChoices;
import forge.game.player.Player;
import forge.game.zone.MagicStack;
/**
@@ -30,14 +37,52 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
continue;
}
boolean preserveNumber = sa.hasParam("PreserveNumber"); // Redirect is not supposed to change number of targets
boolean isSpellskite = sa.hasParam("SpellSkite"); // The only card known to replace targets with self is Spellskite
SpellAbilityStackInstance changingTgtSI = si;
while(changingTgtSI != null) {
// Update targets, with a potential new target
SpellAbility changingTgtSA = changingTgtSI.getSpellAbility();
TargetChoices newTarget = sa.getActivatingPlayer().getController().chooseTargets(changingTgtSA);
changingTgtSI.updateTarget(newTarget);
changingTgtSI = changingTgtSI.getSubInstace();
}
Player chooser = sa.getActivatingPlayer();
if( isSpellskite ) {
Card srcCard = sa.getSourceCard();
// 1. choose a target of target spell
List<Pair<SpellAbilityStackInstance, ITargetable>> allTargets = new ArrayList<>();
while(changingTgtSI != null) {
SpellAbility changedSa = changingTgtSI.getSpellAbility();
if(changedSa.usesTargeting()) {
for(ITargetable it : changedSa.getTargets().getTargets())
allTargets.add(ImmutablePair.of(changingTgtSI, it));
}
changingTgtSI = changingTgtSI.getSubInstace();
}
if( allTargets.isEmpty() ) {
System.err.println("Player managed to target a spell without targets with Spellskite's ability.");
return;
}
Pair<SpellAbilityStackInstance, ITargetable> chosenTarget = chooser.getController().chooseTarget(sa, allTargets);
// 2. replace with spellskite
SpellAbilityStackInstance replaceIn = chosenTarget.getKey();
ITargetable oldTarget = chosenTarget.getValue();
TargetChoices oldTargetBlock = replaceIn.getTargetChoices();
TargetChoices newTargetBlock = oldTargetBlock.clone();
newTargetBlock.remove(oldTarget);
replaceIn.updateTarget(newTargetBlock);
// 3. test if replacing is correct.
if(replaceIn.getSpellAbility().canTarget(srcCard)) {
newTargetBlock.add(srcCard);
replaceIn.updateTarget(newTargetBlock);
} else
replaceIn.updateTarget(oldTargetBlock);
} else
while(changingTgtSI != null) {
// Update targets, with a potential new target
SpellAbility changingTgtSA = changingTgtSI.getSpellAbility();
TargetChoices newTarget = sa.getActivatingPlayer().getController().chooseNewTargetsFor(changingTgtSA);
if ( null != newTarget)
changingTgtSI.updateTarget(newTarget);
changingTgtSI = changingTgtSI.getSubInstace();
}
if (remember) {
sa.getSourceCard().addRemembered(tgtSA.getSourceCard());

View File

@@ -34,7 +34,7 @@ import forge.game.player.Player;
* @author Forge
* @version $Id$
*/
public class TargetChoices {
public class TargetChoices implements Cloneable {
private int numTargeted = 0;
// Card or Player are legal targets.
@@ -140,9 +140,7 @@ public class TargetChoices {
public final boolean remove(final ITargetable target) {
// remove returns true if element was found in given list
if (this.targetCards.remove(target) || this.targetPlayers.remove(target) || this.targetSpells.remove(target)) {
// Do I decrement numTargeted for fizzling targets?
//this.numTargeted--;
this.numTargeted--;
return true;
}
return false;
@@ -255,4 +253,13 @@ public class TargetChoices {
public final boolean isEmpty() {
return targetCards.isEmpty() && targetSpells.isEmpty() && targetPlayers.isEmpty();
}
@Override
public TargetChoices clone() {
TargetChoices tc = new TargetChoices();
tc.targetCards.addAll(this.targetCards);
tc.targetPlayers.addAll(this.targetPlayers);
tc.targetSpells.addAll(this.targetSpells);
return tc;
}
}

View File

@@ -107,7 +107,7 @@ public class TargetSelection {
return this.chooseCardFromStack(mandatory);
} else {
List<Card> validTargets = this.getValidCardsToTarget();
if (zone.size() == 1 && zone.get(0) == ZoneType.Battlefield) {
if (zone.size() == 1 && (zone.get(0) == ZoneType.Battlefield || zone.get(0) == ZoneType.Hand)) {
InputSelectTargets inp = new InputSelectTargets(validTargets, ability, mandatory);
Singletons.getControl().getInputQueue().setInputAndWait(inp);
choiceResult = !inp.hasCancelled();

View File

@@ -491,7 +491,7 @@ public class AiAttackController {
// I know this is a little confusing
Game game = ai.getGame();
random.setSeed(game.getPhaseHandler().getTurn() + this.randomInt);
random.setSeed(game.getPhaseHandler().getTurn() + AiAttackController.randomInt);
final Combat combat = new Combat();
combat.setAttackingPlayer(game.getCombat().getAttackingPlayer());

View File

@@ -6,13 +6,16 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import forge.Card;
import forge.GameEntity;
import forge.ITargetable;
import forge.card.cost.Cost;
import forge.card.mana.Mana;
import forge.card.replacement.ReplacementEffect;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.spellability.TargetChoices;
import forge.deck.Deck;
import forge.game.Game;
@@ -101,7 +104,10 @@ public abstract class PlayerController {
public abstract Integer announceRequirements(SpellAbility ability, String announce, boolean allowZero);
public abstract List<Card> choosePermanentsToSacrifice(SpellAbility sa, int min, int max, List<Card> validTargets, String message);
public abstract List<Card> choosePermanentsToDestroy(SpellAbility sa, int min, int max, List<Card> validTargets, String message);
public abstract TargetChoices chooseTargets(SpellAbility ability);
public abstract TargetChoices chooseNewTargetsFor(SpellAbility ability);
// Specify a target of a spell (Spellskite)
public abstract Pair<SpellAbilityStackInstance, ITargetable> chooseTarget(SpellAbility sa, List<Pair<SpellAbilityStackInstance, ITargetable>> allTargets);
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

@@ -8,6 +8,7 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Lists;
@@ -15,6 +16,7 @@ import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.GameEntity;
import forge.ITargetable;
import forge.card.ability.ApiType;
import forge.card.cost.Cost;
import forge.card.mana.Mana;
@@ -23,6 +25,7 @@ import forge.card.spellability.Ability;
import forge.card.spellability.AbilityStatic;
import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.spellability.TargetChoices;
import forge.deck.Deck;
import forge.game.Game;
@@ -255,7 +258,7 @@ public class PlayerControllerAi extends PlayerController {
* @see forge.game.player.PlayerController#chooseTargets(forge.card.spellability.SpellAbility, forge.card.spellability.SpellAbilityStackInstance)
*/
@Override
public TargetChoices chooseTargets(SpellAbility ability) {
public TargetChoices chooseNewTargetsFor(SpellAbility ability) {
// AI currently can't do this. But when it can it will need to be based on Ability API
return null;
}
@@ -378,4 +381,10 @@ public class PlayerControllerAi extends PlayerController {
return results[i];
}
}
@Override
public Pair<SpellAbilityStackInstance, ITargetable> chooseTarget(SpellAbility saSrc, List<Pair<SpellAbilityStackInstance, ITargetable>> allTargets) {
// TODO Teach AI how to use Spellskite
return allTargets.get(0);
}
}

View File

@@ -11,16 +11,21 @@ import javax.swing.JOptionPane;
import org.apache.commons.lang.math.IntRange;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.Card;
import forge.GameEntity;
import forge.ITargetable;
import forge.Singletons;
import forge.card.cost.Cost;
import forge.card.mana.Mana;
import forge.card.replacement.ReplacementEffect;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.spellability.TargetSelection;
import forge.card.spellability.TargetChoices;
import forge.deck.CardPool;
@@ -440,7 +445,7 @@ public class PlayerControllerHuman extends PlayerController {
* @see forge.game.player.PlayerController#chooseTargets(forge.card.spellability.SpellAbility, forge.card.spellability.SpellAbilityStackInstance)
*/
@Override
public TargetChoices chooseTargets(SpellAbility ability) {
public TargetChoices chooseNewTargetsFor(SpellAbility ability) {
if (ability.getTargetRestrictions() == null) {
return null;
}
@@ -623,4 +628,21 @@ public class PlayerControllerHuman extends PlayerController {
public String chooseFilpResult(Card source, Player flipper, String[] results, boolean call) {
return GuiChoose.one(source.getName() + " - Choose a result", results);
}
@Override
public Pair<SpellAbilityStackInstance, ITargetable> chooseTarget(SpellAbility saSpellskite, List<Pair<SpellAbilityStackInstance, ITargetable>> allTargets) {
if( allTargets.size() < 2)
return Iterables.getFirst(allTargets, null);
final Function<Pair<SpellAbilityStackInstance, ITargetable>, String> fnToString = new Function<Pair<SpellAbilityStackInstance, ITargetable>, String>() {
@Override
public String apply(Pair<SpellAbilityStackInstance, ITargetable> targ) {
return targ.getRight().toString() + " - " + targ.getLeft().getStackDescription();
}
};
List<Pair<SpellAbilityStackInstance, ITargetable>> chosen = GuiChoose.getChoices(saSpellskite.getSourceCard().getName(), 1, 1, allTargets, null, fnToString);
return Iterables.getFirst(chosen, null);
}
}

View File

@@ -17,6 +17,8 @@ import javax.swing.WindowConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.google.common.base.Function;
import forge.Card;
import forge.FThreads;
import forge.Singletons;
@@ -89,19 +91,19 @@ public class GuiChoose {
}
public static <T> List<T> noneOrMany(final String message, final Collection<T> choices) {
return GuiChoose.getChoices(message, 0, choices.size(), choices, null);
return GuiChoose.getChoices(message, 0, choices.size(), choices, null, null);
}
// returned Object will never be null
public static <T> List<T> getChoices(final String message, final int min, final int max, final T[] choices) {
return getChoices(message, min, max, Arrays.asList(choices), null);
return getChoices(message, min, max, Arrays.asList(choices), null, null);
}
public static <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices) {
return getChoices(message, min, max, choices, null);
return getChoices(message, min, max, choices, null, null);
}
public static <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices,final T selected) {
public static <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices, final T selected, final Function<T, String> display) {
if (null == choices || choices.isEmpty()) {
if (0 == min) {
return new ArrayList<T>();
@@ -113,7 +115,7 @@ public class GuiChoose {
Callable<List<T>> showChoice = new Callable<List<T>>() {
@Override
public List<T> call() {
ListChooser<T> c = new ListChooser<T>(message, min, max, choices);
ListChooser<T> c = new ListChooser<T>(message, min, max, choices, display);
final JList<T> list = c.getJList();
list.addListSelectionListener(new ListSelectionListener() {
@Override

View File

@@ -18,6 +18,7 @@
package forge.gui;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
@@ -29,16 +30,19 @@ import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.Action;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import forge.FThreads;
@@ -84,7 +88,7 @@ public class ListChooser<T> {
private JOptionPane optionPane;
private Action ok, cancel;
public ListChooser(final String title, final int minChoices, final int maxChoices, final Collection<T> list) {
public ListChooser(final String title, final int minChoices, final int maxChoices, final Collection<T> list, final Function<T, String> display) {
FThreads.assertExecutedByEdt(true);
this.title = title;
this.minChoices = minChoices;
@@ -104,6 +108,9 @@ public class ListChooser<T> {
if (maxChoices == 1) {
this.jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
if( null != display )
this.jList.setCellRenderer(new TransformedCellRenderer(display));
this.optionPane = new JOptionPane(new JScrollPane(this.jList), JOptionPane.QUESTION_MESSAGE,
JOptionPane.DEFAULT_OPTION, null, options, options[0]);
@@ -296,4 +303,30 @@ public class ListChooser<T> {
}
}
}
private class TransformedCellRenderer implements ListCellRenderer<T> {
public final Function<T, String> transformer;
public final DefaultListCellRenderer defRenderer;
/**
* TODO: Write javadoc for Constructor.
*/
public TransformedCellRenderer(final Function<T, String> t1) {
transformer = t1;
defRenderer = new DefaultListCellRenderer();
}
/* (non-Javadoc)
* @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
*/
@Override
public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected,
boolean cellHasFocus) {
// TODO Auto-generated method stub
return defRenderer.getListCellRendererComponent(list, transformer.apply(value), index, isSelected, cellHasFocus);
}
}
}

View File

@@ -550,7 +550,7 @@ public class QuestWinLose extends ControlWinLose {
Collections.sort(formats);
final GameFormat selected = GuiChoose.getChoices("Choose bonus booster format", 1, 1, formats, pref).get(0); //ch.getSelectedValue();
final GameFormat selected = GuiChoose.getChoices("Choose bonus booster format", 1, 1, formats, pref, null).get(0); //ch.getSelectedValue();
Singletons.getModel().getQuestPreferences().setPref(QPref.BOOSTER_FORMAT, selected.toString());
cardsWon = qData.getCards().generateQuestBooster(selected.getFilterPrinted());

View File

@@ -15,7 +15,7 @@ public class ListChooserTest {
*/
@Test(groups = { "UnitTest", "fast" }, timeOut = 1000, enabled = false)
public void listChooserTest1() {
final ListChooser<String> c = new ListChooser<String>("choose a or b", 0, 2, Arrays.asList("a", "b"));
final ListChooser<String> c = new ListChooser<String>("choose a or b", 0, 2, Arrays.asList("a", "b"), null);
System.out.println(c.show());
for (final String s : c.getSelectedValues()) {
System.out.println(s);