mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
- AF_Counter now uses actual Target object. AI seems to be casting away properly now.
- Converted all of the Counter SAs to use the new Target structure
This commit is contained in:
@@ -168,6 +168,27 @@ public class ComputerAI_General implements Computer {
|
||||
return getPlayable(all);
|
||||
}
|
||||
|
||||
private ArrayList<SpellAbility> getPossibleCounters(){
|
||||
CardList all = new CardList();
|
||||
all.addAll(AllZone.Computer_Hand.getCards());
|
||||
all.addAll(AllZone.Computer_Battlefield.getCards());
|
||||
all.addAll(CardFactoryUtil.getFlashbackCards(AllZone.ComputerPlayer).toArray());
|
||||
|
||||
|
||||
CardList humanPlayable = new CardList();
|
||||
humanPlayable.addAll(AllZone.Human_Battlefield.getCards());
|
||||
humanPlayable = humanPlayable.filter(new CardListFilter()
|
||||
{
|
||||
public boolean addCard(Card c)
|
||||
{
|
||||
return (c.canAnyPlayerActivate());
|
||||
}
|
||||
});
|
||||
all.addAll(humanPlayable.toArray());
|
||||
|
||||
return getPlayableCounters(all);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the spellAbilities from the card list that the computer is able to play
|
||||
*/
|
||||
@@ -175,6 +196,7 @@ public class ComputerAI_General implements Computer {
|
||||
ArrayList<SpellAbility> spellAbility = new ArrayList<SpellAbility>();
|
||||
for(Card c:l)
|
||||
for(SpellAbility sa:c.getSpellAbility())
|
||||
// if SA is from AF_Counter don't add to getPlayable
|
||||
//This try/catch should fix the "computer is thinking" bug
|
||||
try {
|
||||
sa.setActivatingPlayer(AllZone.ComputerPlayer);
|
||||
@@ -187,6 +209,16 @@ public class ComputerAI_General implements Computer {
|
||||
return spellAbility.toArray(new SpellAbility[spellAbility.size()]);
|
||||
}
|
||||
|
||||
private ArrayList<SpellAbility> getPlayableCounters(CardList l) {
|
||||
ArrayList<SpellAbility> spellAbility = new ArrayList<SpellAbility>();
|
||||
for(Card c:l)
|
||||
for(SpellAbility sa:c.getSpellAbility())
|
||||
if (sa.getAbilityFactory() != null && sa.getAbilityFactory().getAPI().equals("Counter"))
|
||||
spellAbility.add(sa);
|
||||
|
||||
return spellAbility;
|
||||
}
|
||||
|
||||
public void begin_combat() {
|
||||
stackResponse();
|
||||
}
|
||||
@@ -270,20 +302,27 @@ public class ComputerAI_General implements Computer {
|
||||
return;
|
||||
}
|
||||
|
||||
// top of stack is owned by human,
|
||||
// top of stack is owned by human,
|
||||
ArrayList<SpellAbility> possibleCounters = getPossibleCounters();
|
||||
sas = getOtherPhases();
|
||||
|
||||
if (sas.length > 0){
|
||||
if (possibleCounters.size() > 0 && ComputerUtil.playCounterSpell(possibleCounters)){
|
||||
// Responding CounterSpell is on the Stack trying to Counter the Spell
|
||||
// If playCounterSpell returns true, a Spell is hitting the Stack
|
||||
return;
|
||||
}
|
||||
else if (sas.length > 0){
|
||||
// Spell not Countered
|
||||
// each AF should check the Stack/Phase on it's own
|
||||
|
||||
//ArrayList<Object> targets = topSA.getTarget().getTargets();
|
||||
// does it target me or something I own?
|
||||
// can i protect it? can I counter it?
|
||||
|
||||
// if i can't save it, can I activate an ability on that card in response? sacrifice etc?
|
||||
|
||||
// does it target his stuff? can I kill it in response?
|
||||
|
||||
|
||||
//ArrayList<Object> targets = topSA.getTarget().getTargets();
|
||||
// does it target me or something I own?
|
||||
// can i protect it? can I counter it?
|
||||
|
||||
// if i can't save it, can I activate an ability on that card in response? sacrifice etc?
|
||||
|
||||
// does it target his stuff? can I kill it in response?
|
||||
}
|
||||
// if this hasn't been covered above, just PassPriority()
|
||||
AllZone.Phase.passPriority();
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
package forge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import forge.card.cardFactory.CardFactoryUtil;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
|
||||
public class ComputerAI_counterSpells2 {
|
||||
|
||||
public static ArrayList<String> KeywordedCounterspells = new ArrayList<String>();
|
||||
|
||||
public static void counter_Spell(SpellAbility sa)
|
||||
{
|
||||
CardList counterSpells = getPlayableCounterSpells(sa);
|
||||
|
||||
boolean countered = false;
|
||||
for (Card var:counterSpells)
|
||||
{
|
||||
if (countered)
|
||||
break;
|
||||
else if (CardUtil.getConvertedManaCost(var.getManaCost()) <= CardUtil.getConvertedManaCost(sa) ||
|
||||
(var.getName().equals("Overwhelming Intellect") && CardUtil.getConvertedManaCost(sa) >= 3 ))
|
||||
{
|
||||
SpellAbility sp = var.getSpellAbility()[0];
|
||||
//ComputerUtil.playNoStack(sp);
|
||||
ComputerUtil.playStack(sp);
|
||||
countered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CardList getPlayableCounterSpells(final SpellAbility sa){
|
||||
final String[] basic = {
|
||||
"Mystic Snake"/*, "Force of Will", "Thwart" */
|
||||
};
|
||||
|
||||
final String[] creature = {
|
||||
"Overwhelming Intellect"
|
||||
};
|
||||
|
||||
final String[] nonCreatureUnlessPay2 = {
|
||||
"Spell Pierce"
|
||||
};
|
||||
|
||||
final String[] unlessPay1 = {
|
||||
"Force Spike", "Daze", "Runeboggle", "Spell Snip"
|
||||
};
|
||||
|
||||
final String[] unlessPay3 = {
|
||||
"Mana Leak"
|
||||
};
|
||||
|
||||
final String[] unlessPay4 = {
|
||||
"Convolute"
|
||||
};
|
||||
|
||||
final int usableManaSources = CardFactoryUtil.getUsableManaSources(AllZone.HumanPlayer);
|
||||
|
||||
PlayerZone hand = AllZone.getZone(Constant.Zone.Hand, AllZone.ComputerPlayer);
|
||||
CardList list = new CardList(hand.getCards());
|
||||
list = list.filter(new CardListFilter()
|
||||
{
|
||||
public boolean addCard(Card c)
|
||||
{
|
||||
if(KeywordedCounterspells.contains(c.getName()))
|
||||
{
|
||||
System.out.println("Counterspell is keyworded, casting is a-go.");
|
||||
return c.getSpells().get(0).canPlayAI();
|
||||
}
|
||||
|
||||
if (usableManaSources == 0) {
|
||||
if (checkArray(c, unlessPay1))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (usableManaSources < 3) {
|
||||
if (checkArray(c, unlessPay3))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (usableManaSources < 4) {
|
||||
if (checkArray(c, unlessPay4))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sa.getSourceCard().isCreature()) {
|
||||
if (checkArray(c, creature))
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (!sa.getSourceCard().isCreature() && usableManaSources < 2) {
|
||||
if (checkArray(c, nonCreatureUnlessPay2))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (checkArray(c, basic))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
public static boolean checkArray(Card c, String[] type)
|
||||
{
|
||||
if (c.getSpellAbility().length == 0)
|
||||
return false;
|
||||
for(String s : type)
|
||||
{
|
||||
SpellAbility sa = c.getSpellAbility()[0];
|
||||
|
||||
if (s.equals(c.getName()) && ComputerUtil.canPayCost(sa) && !c.isUnCastable())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.swing.text.html.MinimalHTMLWriter;
|
||||
|
||||
import forge.card.abilityFactory.AbilityFactory;
|
||||
import forge.card.cardFactory.CardFactoryUtil;
|
||||
import forge.card.mana.ManaCost;
|
||||
@@ -33,6 +35,12 @@ public class ComputerUtil
|
||||
// MyRandom.shuffle(all);
|
||||
|
||||
for(SpellAbility sa : all){
|
||||
// Don't add Counterspells to the "normal" playcard lookupss
|
||||
AbilityFactory af = sa.getAbilityFactory();
|
||||
if (af != null && af.getAPI().equals("Counter"))
|
||||
continue;
|
||||
|
||||
|
||||
sa.setActivatingPlayer(AllZone.ComputerPlayer);
|
||||
if(canPayCost(sa) && sa.canPlay() && sa.canPlayAI())
|
||||
{
|
||||
@@ -65,6 +73,112 @@ public class ComputerUtil
|
||||
return true;
|
||||
}//playCards()
|
||||
|
||||
static public int counterSpellRestriction(SpellAbility sa){
|
||||
// Move this to AF?
|
||||
// Restriction Level is Based off a handful of factors
|
||||
|
||||
int restrict = 0;
|
||||
|
||||
Card source = sa.getSourceCard();
|
||||
Target tgt = sa.getTarget();
|
||||
AbilityFactory af = sa.getAbilityFactory();
|
||||
HashMap<String, String> params = af.getMapParams();
|
||||
|
||||
// Play higher costing spells first?
|
||||
Cost cost = sa.getPayCosts();
|
||||
// Convert cost to CMC
|
||||
String totalMana = source.getSVar("PayX"); // + cost.getCMC()
|
||||
|
||||
// Consider the costs here for relative "scoring"
|
||||
if (cost.getDiscardType().equals("Hand")){
|
||||
// Null Brooch aid
|
||||
restrict -= (AllZoneUtil.getPlayerHand(AllZone.ComputerPlayer).size() * 20);
|
||||
}
|
||||
|
||||
// Abilities before Spells (card advantage)
|
||||
if (af.isAbility())
|
||||
restrict += 40;
|
||||
|
||||
// TargetValidTargeting gets biggest bonus
|
||||
if (tgt.getSAValidTargeting() != null){
|
||||
restrict += 35;
|
||||
}
|
||||
|
||||
// Unless Cost gets significant bonus + 10-Payment Amount
|
||||
String unless = params.get("UnlessCost");
|
||||
if (unless != null){
|
||||
int amount = AbilityFactory.calculateAmount(source, unless, sa);
|
||||
restrict += 20 - (2*amount);
|
||||
}
|
||||
|
||||
// Then base on Targeting Restriction
|
||||
String[] validTgts = tgt.getValidTgts();
|
||||
if (validTgts.length != 1 || !validTgts[0].equals("Card"))
|
||||
restrict += 10;
|
||||
|
||||
// And lastly give some bonus points to least restrictive TargetType (Spell,Ability,Triggered)
|
||||
String tgtType = tgt.getTargetSpellAbilityType();
|
||||
restrict -= (5*tgtType.split(",").length);
|
||||
|
||||
return restrict;
|
||||
}
|
||||
|
||||
//if return true, go to next phase
|
||||
static public boolean playCounterSpell(ArrayList<SpellAbility> possibleCounters)
|
||||
{
|
||||
SpellAbility bestSA = null;
|
||||
int bestRestriction = Integer.MIN_VALUE;
|
||||
|
||||
for(SpellAbility sa : possibleCounters){
|
||||
sa.setActivatingPlayer(AllZone.ComputerPlayer);
|
||||
if(canPayCost(sa) && sa.canPlay() && sa.canPlayAI()){
|
||||
if (bestSA == null){
|
||||
bestSA = sa;
|
||||
bestRestriction = counterSpellRestriction(sa);
|
||||
}
|
||||
else{
|
||||
// Compare bestSA with this SA
|
||||
int restrictionLevel = counterSpellRestriction(sa);
|
||||
|
||||
if (restrictionLevel > bestRestriction){
|
||||
bestRestriction = restrictionLevel;
|
||||
bestSA = sa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}//while
|
||||
|
||||
if (bestSA == null)
|
||||
return false;
|
||||
|
||||
// TODO
|
||||
// "Look" at Targeted SA and "calculate" the threshold
|
||||
// if (bestRestriction < targetedThreshold) return false;
|
||||
|
||||
AllZone.Stack.freezeStack();
|
||||
Card source = bestSA.getSourceCard();
|
||||
|
||||
if(bestSA.isSpell() && !source.isCopiedSpell())
|
||||
AllZone.GameAction.moveToStack(source);
|
||||
|
||||
Cost cost = bestSA.getPayCosts();
|
||||
|
||||
if (cost == null){
|
||||
// Honestly Counterspells shouldn't use this branch
|
||||
payManaCost(bestSA);
|
||||
bestSA.chooseTargetAI();
|
||||
bestSA.getBeforePayManaAI().execute();
|
||||
AllZone.Stack.addAndUnfreeze(bestSA);
|
||||
}
|
||||
else{
|
||||
Cost_Payment pay = new Cost_Payment(cost, bestSA);
|
||||
pay.payComputerCosts();
|
||||
}
|
||||
|
||||
return true;
|
||||
}//playCounterSpell()
|
||||
|
||||
|
||||
//this is used for AI's counterspells
|
||||
final static public void playStack(SpellAbility sa)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ import forge.card.spellability.Ability_Triggered;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.Spell_Permanent;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.card.spellability.Target_Selection;
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.gui.input.Input;
|
||||
import forge.gui.input.Input_PayManaCost_Ability;
|
||||
@@ -634,12 +635,8 @@ public class MagicStack extends MyObservable {
|
||||
}
|
||||
}
|
||||
// attempt to counter human spell
|
||||
// TODO this needs to move to the stack response section for the AI
|
||||
if (sp.getSourceCard().getController().equals(AllZone.HumanPlayer) && CardFactoryUtil.isCounterable(sp.getSourceCard()))
|
||||
ComputerAI_counterSpells2.counter_Spell(sp);
|
||||
|
||||
GameActionUtil.executePlayCardEffects(sp);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,6 +756,10 @@ public class MagicStack extends MyObservable {
|
||||
Card card = (Card)o;
|
||||
fizzle &= !(CardFactoryUtil.isTargetStillValid(fizzSA, card));
|
||||
}
|
||||
if (o instanceof SpellAbility){
|
||||
SpellAbility tgtSA = (SpellAbility)o;
|
||||
fizzle &= !(Target_Selection.matchSpellAbility(fizzSA, tgtSA, tgt));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fizzSA.getTargetCard() != null) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import forge.AllZone;
|
||||
import forge.AllZoneUtil;
|
||||
import forge.Card;
|
||||
import forge.CardList;
|
||||
import forge.ComputerAI_counterSpells2;
|
||||
import forge.Constant;
|
||||
import forge.Player;
|
||||
import forge.card.cardFactory.CardFactoryUtil;
|
||||
@@ -95,6 +94,9 @@ public class AbilityFactory {
|
||||
return hasSpDesc;
|
||||
}
|
||||
|
||||
private String API = "";
|
||||
public String getAPI() { return API; }
|
||||
|
||||
//*******************************************************
|
||||
|
||||
public HashMap<String,String> getMapParams(String abString, Card hostCard) {
|
||||
@@ -140,8 +142,7 @@ public class AbilityFactory {
|
||||
mapParams = getMapParams(abString, hostCard);
|
||||
|
||||
// parse universal parameters
|
||||
|
||||
String API = "";
|
||||
|
||||
if (mapParams.containsKey("AB"))
|
||||
{
|
||||
isAb = true;
|
||||
@@ -194,6 +195,15 @@ public class AbilityFactory {
|
||||
|
||||
if (mapParams.containsKey("TgtZone")) // if Targeting something not in play, this Key should be set
|
||||
abTgt.setZone(mapParams.get("TgtZone"));
|
||||
|
||||
// Target Type mostly for Counter: Spell,Activated,Triggered,Ability (or any combination of)
|
||||
// Ability = both activated and triggered abilities
|
||||
if (mapParams.containsKey("TargetType"))
|
||||
abTgt.setTargetSpellAbilityType(mapParams.get("TargetType"));
|
||||
|
||||
// TargetValidTargeting most for Counter: e.g. target spell that targets X.
|
||||
if (mapParams.containsKey("TargetValidTargeting"))
|
||||
abTgt.setSAValidTargeting(mapParams.get("TargetValidTargeting"));
|
||||
}
|
||||
|
||||
hasSubAb = mapParams.containsKey("SubAbility");
|
||||
@@ -261,7 +271,6 @@ public class AbilityFactory {
|
||||
SA = AbilityFactory_Counters.createDrawbackProliferate(this);
|
||||
}
|
||||
|
||||
|
||||
if (API.equals("ChangeZone")){
|
||||
if (isAb)
|
||||
SA = AbilityFactory_ChangeZone.createAbilityChangeZone(this);
|
||||
@@ -280,32 +289,6 @@ public class AbilityFactory {
|
||||
SA = AbilityFactory_ChangeZone.createDrawbackChangeZoneAll(this);
|
||||
}
|
||||
|
||||
// Fetch, Retrieve and Bounce should be converted ChangeZone
|
||||
/*
|
||||
if (API.equals("Fetch")){
|
||||
if (isAb)
|
||||
SA = AbilityFactory_Fetch.createAbilityFetch(this);
|
||||
else if (isSp)
|
||||
SA = AbilityFactory_Fetch.createSpellFetch(this);
|
||||
}
|
||||
|
||||
if (API.equals("Retrieve")){
|
||||
if (isAb)
|
||||
SA = AbilityFactory_Fetch.createAbilityRetrieve(this);
|
||||
else if (isSp)
|
||||
SA = AbilityFactory_Fetch.createSpellRetrieve(this);
|
||||
}
|
||||
|
||||
if (API.equals("Bounce")){
|
||||
if (isAb)
|
||||
SA = AbilityFactory_Bounce.createAbilityBounce(this);
|
||||
else if (isSp)
|
||||
SA = AbilityFactory_Bounce.createSpellBounce(this);
|
||||
hostCard.setSVar("PlayMain1", "TRUE");
|
||||
}
|
||||
*/
|
||||
// Convert above abilities to gain Drawback
|
||||
|
||||
if (API.equals("Pump"))
|
||||
{
|
||||
AbilityFactory_Pump afPump = new AbilityFactory_Pump(this);
|
||||
@@ -546,7 +529,9 @@ public class AbilityFactory {
|
||||
|
||||
if(API.equals("Counter")){
|
||||
AbilityFactory_CounterMagic c = new AbilityFactory_CounterMagic(this);
|
||||
ComputerAI_counterSpells2.KeywordedCounterspells.add(hostC.getName());
|
||||
|
||||
if (isTargeted) // Since all "Counter" ABs Counter things on the Stack no need for it to be everywhere
|
||||
abTgt.setZone("Stack");
|
||||
|
||||
if(isAb)
|
||||
SA = c.getAbilityCounter(this);
|
||||
@@ -969,12 +954,21 @@ public class AbilityFactory {
|
||||
return players;
|
||||
}
|
||||
|
||||
public static ArrayList<SpellAbility> getDefinedSpellAbilities(Card card, String def, SpellAbility sa){
|
||||
ArrayList<SpellAbility> sas = new ArrayList<SpellAbility>();
|
||||
|
||||
// TODO: Define SpellAbilities
|
||||
|
||||
return sas;
|
||||
}
|
||||
|
||||
public static ArrayList<Object> getDefinedObjects(Card card, String def, SpellAbility sa){
|
||||
ArrayList<Object> objects = new ArrayList<Object>();
|
||||
String defined = (def == null) ? "Self" : def;
|
||||
|
||||
objects.addAll(getDefinedPlayers(card, defined, sa));
|
||||
objects.addAll(getDefinedCards(card, defined, sa));
|
||||
objects.addAll(getDefinedSpellAbilities(card, defined, sa));
|
||||
return objects;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,10 @@ import forge.card.spellability.Ability_Activated;
|
||||
import forge.card.spellability.Ability_Sub;
|
||||
import forge.card.spellability.Spell;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.card.spellability.Target_Selection;
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.gui.input.Input;
|
||||
|
||||
//Type - Spell or Ability or SpellOrAbility
|
||||
//CounterValid - a "valid" expression for types to counter
|
||||
//SpellTarget - a "valid" expression for targets of the spell to counter
|
||||
//Destination - send countered spell to: (only applies to Spells; ignored for Abilities)
|
||||
// -Graveyard (Default)
|
||||
// -Exile
|
||||
@@ -37,34 +35,22 @@ import forge.gui.input.Input;
|
||||
//ExtraActions - implemented exactly as spCounter used them (can probably be updated to SubAbility/Drawback), then this param is eliminated
|
||||
|
||||
//Examples:
|
||||
//A:SP$Counter | Cost$ 1 G | Type$ Ability | SpellDescription$ Counter target activated ability.
|
||||
//A:AB$Counter | Cost$ G G | Type$ Spell | Destination$ Exile | CounterValid$ Color.Black | SpellDescription$ xxxxx
|
||||
//A:SP$Counter | Cost$ 1 G | TargetType$ Activated | SpellDescription$ Counter target activated ability.
|
||||
//A:AB$Counter | Cost$ G G | TargetType$ Spell | Destination$ Exile | ValidTgts$ Color.Black | SpellDescription$ xxxxx
|
||||
|
||||
public class AbilityFactory_CounterMagic {
|
||||
|
||||
private AbilityFactory af = null;
|
||||
private HashMap<String,String> params = null;
|
||||
private String targetType = null;
|
||||
private String destination = null;
|
||||
private String[] splitTargetingRestrictions = null;
|
||||
private String[] splitSpellTargetRestrictions = null;
|
||||
private String[] splitExtraActions;
|
||||
private String unlessCost = null;
|
||||
|
||||
private final SpellAbility[] tgt = new SpellAbility[1];
|
||||
|
||||
public AbilityFactory_CounterMagic(AbilityFactory newAF) {
|
||||
af = newAF;
|
||||
params = af.getMapParams();
|
||||
targetType = params.containsKey("Type") ? params.get("Type") : "Spell";
|
||||
|
||||
destination = params.containsKey("Destination") ? params.get("Destination") : "Graveyard";
|
||||
if(params.containsKey("CounterValid")) {
|
||||
splitTargetingRestrictions = params.get("CounterValid").split(",");
|
||||
}
|
||||
else splitTargetingRestrictions = new String[] {"Card"};
|
||||
if(params.containsKey("SpellTarget")) {
|
||||
splitSpellTargetRestrictions = params.get("SpellTarget").split(",");
|
||||
}
|
||||
if(params.containsKey("ExtraActions")) {
|
||||
splitExtraActions = params.get("ExtraActions").split(" ");
|
||||
}
|
||||
@@ -73,7 +59,6 @@ public class AbilityFactory_CounterMagic {
|
||||
if(params.containsKey("UnlessCost"))
|
||||
unlessCost = params.get("UnlessCost").trim();
|
||||
|
||||
tgt[0] = null;
|
||||
}
|
||||
|
||||
public SpellAbility getAbilityCounter(final AbilityFactory AF) {
|
||||
@@ -86,13 +71,6 @@ public class AbilityFactory_CounterMagic {
|
||||
return counterStackDescription(af, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlay() {
|
||||
// super takes care of AdditionalCosts
|
||||
//important to keep super.canPlay() first due to targeting hack in counterCanPlay
|
||||
return super.canPlay() && counterCanPlay(af, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlayAI() {
|
||||
return counterCanPlayAI(af, this);
|
||||
@@ -121,13 +99,6 @@ public class AbilityFactory_CounterMagic {
|
||||
return counterStackDescription(af, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlay() {
|
||||
// super takes care of AdditionalCosts
|
||||
//important to keep super.canPlay() first due to targeting hack in counterCanPlay
|
||||
return super.canPlay() && counterCanPlay(af, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlayAI() {
|
||||
return counterCanPlayAI(af, this);
|
||||
@@ -141,63 +112,121 @@ public class AbilityFactory_CounterMagic {
|
||||
};
|
||||
return spCounter;
|
||||
}
|
||||
|
||||
// Add Counter Drawback
|
||||
|
||||
private void counterResolve(final AbilityFactory af, final SpellAbility sa) {
|
||||
private boolean counterCanPlayAI(final AbilityFactory af, final SpellAbility sa){
|
||||
boolean toReturn = true;
|
||||
if(AllZone.Stack.size() < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SpellAbility topSA = AllZone.Stack.peek();
|
||||
if (!CardFactoryUtil.isCounterable(topSA.getSourceCard()))
|
||||
return false;
|
||||
|
||||
Target tgt = sa.getTarget();
|
||||
if (Target_Selection.matchSpellAbility(sa, topSA, tgt))
|
||||
tgt.addTarget(topSA);
|
||||
|
||||
else
|
||||
return false;
|
||||
|
||||
Card source = sa.getSourceCard();
|
||||
//copied from spCounter
|
||||
if(matchSpellAbility(sa.getSourceCard(), tgt[0], splitTargetingRestrictions, splitSpellTargetRestrictions, targetType)
|
||||
&& AllZone.Stack.contains(tgt[0])
|
||||
&& !tgt[0].getSourceCard().keywordsContain("CARDNAME can't be countered.")) {
|
||||
final SpellAbility tgtSA = tgt[0];
|
||||
Card tgtSACard = tgtSA.getSourceCard();
|
||||
|
||||
System.out.println("Send countered spell to " + destination);
|
||||
|
||||
if(unlessCost != null) {
|
||||
String unlessCostFinal = unlessCost;
|
||||
if(unlessCost.equals("X"))
|
||||
{
|
||||
unlessCostFinal = Integer.toString(CardFactoryUtil.xCount(af.getHostCard(), af.getHostCard().getSVar("X")));
|
||||
}
|
||||
|
||||
Ability ability = new Ability(af.getHostCard(), unlessCostFinal) {
|
||||
@Override
|
||||
public void resolve() {
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
final Command unpaidCommand = new Command() {
|
||||
private static final long serialVersionUID = 8094833091127334678L;
|
||||
|
||||
public void execute() {
|
||||
removeFromStack(tgtSA,sa);
|
||||
if(params.containsKey("PowerSink")) doPowerSink(AllZone.HumanPlayer);
|
||||
}
|
||||
};
|
||||
|
||||
if(AllZone.Stack.peek().getActivatingPlayer().isHuman()) {
|
||||
GameActionUtil.payManaDuringAbilityResolve(af.getHostCard() + "\r\n", ability.getManaCost(),
|
||||
Command.Blank, unpaidCommand);
|
||||
} else {
|
||||
if(ComputerUtil.canPayCost(ability)) ComputerUtil.playNoStack(ability);
|
||||
else {
|
||||
removeFromStack(tgtSA,sa);
|
||||
if(params.containsKey("PowerSink")) doPowerSink(AllZone.ComputerPlayer);
|
||||
}
|
||||
}
|
||||
doExtraActions(tgtSA,sa);
|
||||
if (unlessCost != null){
|
||||
// Is this Usable Mana Sources? Or Total Available Mana?
|
||||
int usableManaSources = CardFactoryUtil.getUsableManaSources(AllZone.HumanPlayer);
|
||||
int toPay = 0;
|
||||
boolean setPayX = false;
|
||||
if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")){
|
||||
setPayX = true;
|
||||
toPay = ComputerUtil.determineLeftoverMana(sa);
|
||||
}
|
||||
else
|
||||
{
|
||||
removeFromStack(tgtSA,sa);
|
||||
doExtraActions(tgtSA,sa);
|
||||
}
|
||||
toPay = AbilityFactory.calculateAmount(source, unlessCost, sa);
|
||||
|
||||
if(tgtSA.isAbility() && params.containsKey("DestroyPermanent")) {
|
||||
AllZone.GameAction.destroy(tgtSACard);
|
||||
}
|
||||
if (toPay == 0 || toPay <= usableManaSources)
|
||||
return false;
|
||||
|
||||
if (setPayX)
|
||||
source.setSVar("PayX", Integer.toString(toPay));
|
||||
}
|
||||
|
||||
// TODO: Improve AI
|
||||
|
||||
// Will return true if this spell can counter (or will likely counter) and false if it can't
|
||||
|
||||
// But really it should be more picky about how it counters things
|
||||
|
||||
Ability_Sub subAb = sa.getSubAbility();
|
||||
if (subAb != null)
|
||||
toReturn &= subAb.chkAI_Drawback();
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
|
||||
private void counterResolve(final AbilityFactory af, final SpellAbility sa) {
|
||||
// TODO: Before this resolves we should see if any of our targets are still on the stack
|
||||
Card source = sa.getSourceCard();
|
||||
Target tgt = sa.getTarget();
|
||||
|
||||
ArrayList<SpellAbility> sas = tgt.getTargetSAs();
|
||||
|
||||
if (sas == null){
|
||||
sas = AbilityFactory.getDefinedSpellAbilities(sa.getSourceCard(), params.get("Defined"), sa);
|
||||
}
|
||||
|
||||
for(final SpellAbility tgtSA : sas){
|
||||
Card tgtSACard = tgtSA.getSourceCard();
|
||||
if (AllZone.Stack.contains(tgtSA) && !tgtSACard.keywordsContain("CARDNAME can't be countered.")){
|
||||
|
||||
// TODO: Unless Cost should be generalized for all AFS
|
||||
if(unlessCost != null) {
|
||||
String unlessCostFinal = unlessCost;
|
||||
if(unlessCost.equals("X"))
|
||||
unlessCostFinal = Integer.toString(CardFactoryUtil.xCount(af.getHostCard(), af.getHostCard().getSVar("X")));
|
||||
// Above xCount should probably be changed to a AF.calculateAmount
|
||||
|
||||
Ability ability = new Ability(af.getHostCard(), unlessCostFinal) {
|
||||
@Override
|
||||
public void resolve() {
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
final Command unpaidCommand = new Command() {
|
||||
private static final long serialVersionUID = 8094833091127334678L;
|
||||
|
||||
public void execute() {
|
||||
removeFromStack(tgtSA, sa);
|
||||
if(params.containsKey("PowerSink")) doPowerSink(AllZone.HumanPlayer);
|
||||
}
|
||||
};
|
||||
|
||||
if(tgtSA.getActivatingPlayer().isHuman()) {
|
||||
GameActionUtil.payManaDuringAbilityResolve(af.getHostCard() + "\r\n", ability.getManaCost(),
|
||||
Command.Blank, unpaidCommand);
|
||||
} else {
|
||||
if(ComputerUtil.canPayCost(ability))
|
||||
ComputerUtil.playNoStack(ability);
|
||||
|
||||
else {
|
||||
removeFromStack(tgtSA,sa);
|
||||
if(params.containsKey("PowerSink")) doPowerSink(AllZone.ComputerPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
removeFromStack(tgtSA,sa);
|
||||
|
||||
// Destroy Permanent may be able to be turned into a SubAbility
|
||||
if(tgtSA.isAbility() && params.containsKey("DestroyPermanent")) {
|
||||
AllZone.GameAction.destroy(tgtSACard);
|
||||
}
|
||||
}
|
||||
// Do Extra Actions whether or not the spell was actually countered
|
||||
doExtraActions(tgtSA,sa);
|
||||
}
|
||||
|
||||
if (af.hasSubAbility()){
|
||||
@@ -206,14 +235,11 @@ public class AbilityFactory_CounterMagic {
|
||||
abSub.resolve();
|
||||
}
|
||||
else{
|
||||
//I think UntapUpTo is the main thing holding this back
|
||||
String DrawBack = params.get("SubAbility");
|
||||
CardFactoryUtil.doDrawBack(DrawBack, 0, source.getController(), source.getController().getOpponent(), source.getController(), source, null, sa);
|
||||
}
|
||||
}
|
||||
|
||||
//reset tgts
|
||||
tgt[0] = null;
|
||||
|
||||
}
|
||||
|
||||
private void doPowerSink(Player p) {
|
||||
@@ -240,14 +266,25 @@ public class AbilityFactory_CounterMagic {
|
||||
else
|
||||
sb.append(" ");
|
||||
|
||||
sb.append("countering ");
|
||||
sb.append(tgt[0].getSourceCard().getName());
|
||||
if(tgt[0].isAbility()) sb.append("'s ability.");
|
||||
else sb.append(".");
|
||||
sb.append("countering");
|
||||
|
||||
if(tgt[0].isAbility() && params.containsKey("DestroyPermanent")) {
|
||||
sb.append(" Destroy "+tgt[0].getSourceCard()).append(".");
|
||||
ArrayList<SpellAbility> sas = sa.getTarget().getTargetSAs();
|
||||
if (sas == null)
|
||||
sas = AbilityFactory.getDefinedSpellAbilities(sa.getSourceCard(), params.get("Defined"), sa);
|
||||
|
||||
boolean isAbility = false;
|
||||
for(final SpellAbility tgtSA : sas){
|
||||
sb.append(" ");
|
||||
sb.append(tgtSA.getSourceCard().getName());
|
||||
isAbility = tgtSA.isAbility();
|
||||
if(isAbility) sb.append("'s ability");
|
||||
}
|
||||
|
||||
if(isAbility && params.containsKey("DestroyPermanent")) {
|
||||
sb.append(" and Destroy it");
|
||||
}
|
||||
|
||||
sb.append(".");
|
||||
|
||||
Ability_Sub abSub = sa.getSubAbility();
|
||||
if (abSub != null){
|
||||
@@ -256,57 +293,6 @@ public class AbilityFactory_CounterMagic {
|
||||
|
||||
return sb.toString();
|
||||
}//end counterStackDescription
|
||||
|
||||
private boolean counterCanPlay(final AbilityFactory af, final SpellAbility sa) {
|
||||
ArrayList<SpellAbility> choosables = new ArrayList<SpellAbility>();
|
||||
|
||||
for(int i = 0; i < AllZone.Stack.size(); i++) {
|
||||
choosables.add(AllZone.Stack.peek(i));
|
||||
}
|
||||
|
||||
for(int i = 0; i < choosables.size(); i++) {
|
||||
if(!matchSpellAbility(sa.getSourceCard(), choosables.get(i),
|
||||
splitTargetingRestrictions, splitSpellTargetRestrictions, targetType)) {
|
||||
choosables.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
if(tgt[0] == null && choosables.size() > 0 ) AllZone.InputControl.setInput(getInput(sa));
|
||||
|
||||
return choosables.size() > 0;
|
||||
}
|
||||
|
||||
private boolean counterCanPlayAI(final AbilityFactory af, final SpellAbility sa){
|
||||
boolean toReturn = false;
|
||||
if(AllZone.Stack.size() < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SpellAbility topSA = AllZone.Stack.peek();
|
||||
if (!CardFactoryUtil.isCounterable(topSA.getSourceCard()))
|
||||
return false;
|
||||
|
||||
if(matchSpellAbility(sa.getSourceCard(), topSA, splitTargetingRestrictions, splitSpellTargetRestrictions, targetType)) {
|
||||
tgt[0] = topSA;
|
||||
toReturn = true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
Card source = sa.getSourceCard();
|
||||
if (unlessCost != null && unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")){
|
||||
int xPay = ComputerUtil.determineLeftoverMana(sa);
|
||||
if (xPay == 0) // todo: compare xPay to human's leftover mana
|
||||
return false;
|
||||
source.setSVar("PayX", Integer.toString(xPay));
|
||||
}
|
||||
|
||||
Ability_Sub subAb = sa.getSubAbility();
|
||||
if (subAb != null)
|
||||
toReturn &= subAb.chkAI_Drawback();
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
private void removeFromStack(SpellAbility tgtSA,SpellAbility srcSA)
|
||||
{
|
||||
@@ -336,11 +322,14 @@ public class AbilityFactory_CounterMagic {
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("AbilityFactory_CounterMagic: Invalid Destination argument for card " + srcSA.getSourceCard().getName());
|
||||
}
|
||||
}
|
||||
if (!tgtSA.isAbility())
|
||||
System.out.println("Send countered spell to " + destination);
|
||||
}
|
||||
|
||||
private void doExtraActions(SpellAbility tgtSA,SpellAbility srcSA)
|
||||
{
|
||||
// TODO: Convert Extra Actions
|
||||
for(int ea = 0; ea < splitExtraActions.length; ea++) {
|
||||
boolean isOptional = false;
|
||||
|
||||
@@ -493,125 +482,4 @@ public class AbilityFactory_CounterMagic {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean matchSpellAbility(Card srcCard, SpellAbility sa, String[] splitRestrictions, String[] splitTargetRestrictions, String targetType) {
|
||||
boolean result = true;
|
||||
|
||||
if(targetType.equals("Spell")) {
|
||||
if(sa.isAbility()) {
|
||||
System.out.println(srcCard.getName() + " can only counter spells, not abilities.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(targetType.equals("Ability")) {
|
||||
if(sa.isSpell()) {
|
||||
System.out.println(srcCard.getName() + " can only counter abilities, not spells.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(targetType.equals("SpellOrAbility")) {
|
||||
//Do nothing. This block is only for clarity and enforcing parameters.
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid target type for card " + srcCard.getName());
|
||||
}
|
||||
|
||||
if(splitTargetRestrictions != null)
|
||||
{
|
||||
result = false;
|
||||
if(sa.getTarget() != null)
|
||||
{
|
||||
for(Object o : sa.getTarget().getTargets())
|
||||
{
|
||||
if(matchesValid(o,splitTargetRestrictions,srcCard))
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!matchesValid(sa.getSourceCard(),splitRestrictions,srcCard))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}//matchSpellAbility
|
||||
|
||||
private static boolean matchesValid(Object o,String[] valids,Card srcCard)
|
||||
{
|
||||
if(o instanceof Card)
|
||||
{
|
||||
Card c = (Card)o;
|
||||
return c.isValidCard(valids, srcCard.getController(), srcCard);
|
||||
}
|
||||
|
||||
if(o instanceof Player)
|
||||
{
|
||||
for(String v : valids)
|
||||
{
|
||||
if(v.equalsIgnoreCase("Player"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(v.equalsIgnoreCase("Opponent"))
|
||||
{
|
||||
if(o.equals(srcCard.getController().getOpponent()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(v.equalsIgnoreCase("You"))
|
||||
{
|
||||
return o.equals(srcCard.getController());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Input getInput(final SpellAbility sa) {
|
||||
Input runtime = new Input() {
|
||||
|
||||
private static final long serialVersionUID = 5360660530175041997L;
|
||||
|
||||
@Override
|
||||
public void showMessage() {
|
||||
ArrayList<SpellAbility> choosables = new ArrayList<SpellAbility>();
|
||||
|
||||
for(int i = 0; i < AllZone.Stack.size(); i++) {
|
||||
choosables.add(AllZone.Stack.peek(i));
|
||||
}
|
||||
|
||||
for(int i = 0; i < choosables.size(); i++) {
|
||||
if(!matchSpellAbility(sa.getSourceCard(), choosables.get(i), splitTargetingRestrictions, splitSpellTargetRestrictions, targetType) || choosables.get(i).getSourceCard().equals(sa.getSourceCard())) {
|
||||
choosables.remove(i);
|
||||
}
|
||||
}
|
||||
HashMap<String,SpellAbility> map = new HashMap<String,SpellAbility>();
|
||||
|
||||
for(SpellAbility sa : choosables) {
|
||||
map.put(sa.getStackDescription(),sa);
|
||||
}
|
||||
|
||||
String[] choices = new String[map.keySet().size()];
|
||||
choices = map.keySet().toArray(choices);
|
||||
|
||||
String madeChoice = GuiUtils.getChoice("Select target spell.",choices);
|
||||
|
||||
tgt[0] = map.get(madeChoice);
|
||||
System.out.println(tgt[0]);
|
||||
stop();
|
||||
}//showMessage()
|
||||
};//Input
|
||||
return runtime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import forge.CardListFilter;
|
||||
import forge.CardListUtil;
|
||||
import forge.CardUtil;
|
||||
import forge.Command;
|
||||
import forge.ComputerAI_counterSpells2;
|
||||
import forge.ComputerUtil;
|
||||
import forge.Constant;
|
||||
import forge.Counters;
|
||||
@@ -2055,13 +2054,10 @@ public class CardFactory implements NewConstants {
|
||||
|
||||
if (getSourceCard().getAttachedCards().length > 0)
|
||||
{
|
||||
// Isochron Scepter might be broken?
|
||||
Card c = copyCard(getSourceCard().getAttachedCards()[0]);
|
||||
if (ComputerAI_counterSpells2.KeywordedCounterspells.contains(c.getName()))
|
||||
{
|
||||
SpellAbility sa = c.getSpellAbility()[0];
|
||||
return sa.canPlay();
|
||||
}
|
||||
else return true;
|
||||
SpellAbility sa = c.getSpellAbility()[0];
|
||||
return sa.canPlay();
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
@@ -13,15 +13,8 @@ public class Target {
|
||||
private boolean bMandatory = false;
|
||||
private Card srcCard;
|
||||
|
||||
public boolean getMandatory()
|
||||
{
|
||||
return bMandatory;
|
||||
}
|
||||
|
||||
public void setMandatory(boolean m)
|
||||
{
|
||||
bMandatory = m;
|
||||
}
|
||||
public boolean getMandatory() { return bMandatory; }
|
||||
public void setMandatory(boolean m) { bMandatory = m; }
|
||||
|
||||
private boolean tgtValid = false;
|
||||
private String ValidTgts[];
|
||||
@@ -43,21 +36,30 @@ public class Target {
|
||||
public void setZone(String tZone) { tgtZone = tZone; }
|
||||
public String getZone() { return tgtZone; }
|
||||
|
||||
// Used for Counters. Currently, Spell,Activated,Triggered can be Comma-separated
|
||||
private String targetSpellAbilityType = null;
|
||||
public void setTargetSpellAbilityType(String tgtSAType) { targetSpellAbilityType = tgtSAType; }
|
||||
public String getTargetSpellAbilityType() { return targetSpellAbilityType; }
|
||||
|
||||
// Used for Counters. The target SA of this SA must be targeting a Valid X
|
||||
private String saValidTargeting = null;
|
||||
public void setSAValidTargeting(String saValidTgting) { saValidTargeting = saValidTgting; }
|
||||
public String getSAValidTargeting() { return saValidTargeting; }
|
||||
|
||||
// Card or Player are legal targets.
|
||||
private ArrayList<Card> targetCards = new ArrayList<Card>();
|
||||
private ArrayList<Player> targetPlayers = new ArrayList<Player>();
|
||||
private ArrayList<SpellAbility> targetSAs = new ArrayList<SpellAbility>();
|
||||
|
||||
public void addTarget(Object o){
|
||||
if (o instanceof Player){
|
||||
Player p = (Player)o;
|
||||
if (!targetPlayers.contains(p))
|
||||
targetPlayers.add(p);
|
||||
}
|
||||
if (o instanceof Card){
|
||||
Card c = (Card)o;
|
||||
if (!targetCards.contains(c))
|
||||
targetCards.add(c);
|
||||
}
|
||||
if (o instanceof Player)
|
||||
addTarget((Player)o);
|
||||
|
||||
else if (o instanceof Card)
|
||||
addTarget((Card)o);
|
||||
|
||||
else if (o instanceof SpellAbility)
|
||||
addTarget((SpellAbility)o);
|
||||
}
|
||||
|
||||
public boolean addTarget(Card c){
|
||||
@@ -77,6 +79,15 @@ public class Target {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean addTarget(SpellAbility sa){
|
||||
if (!targetSAs.contains(sa)){
|
||||
targetSAs.add(sa);
|
||||
numTargeted++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ArrayList<Card> getTargetCards(){
|
||||
return targetCards;
|
||||
@@ -86,10 +97,15 @@ public class Target {
|
||||
return targetPlayers;
|
||||
}
|
||||
|
||||
public ArrayList<SpellAbility> getTargetSAs(){
|
||||
return targetSAs;
|
||||
}
|
||||
|
||||
public ArrayList<Object> getTargets(){
|
||||
ArrayList<Object> tgts = new ArrayList<Object>();
|
||||
tgts.addAll(targetPlayers);
|
||||
tgts.addAll(targetCards);
|
||||
tgts.addAll(targetSAs);
|
||||
|
||||
return tgts;
|
||||
}
|
||||
@@ -101,6 +117,7 @@ public class Target {
|
||||
numTargeted = 0;
|
||||
targetCards.clear();
|
||||
targetPlayers.clear();
|
||||
targetSAs.clear();
|
||||
}
|
||||
|
||||
public Target(Card src,String parse){
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.card.spellability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import forge.AllZone;
|
||||
import forge.AllZoneUtil;
|
||||
@@ -76,14 +77,10 @@ public class Target_Selection {
|
||||
}
|
||||
|
||||
//targets still needed
|
||||
if(target.getMandatory())
|
||||
{
|
||||
AllZone.InputControl.setInput(input_targetValid(ability, target.getValidTgts(), target.getVTSelection(), this, req, target.hasCandidates()));
|
||||
}
|
||||
else
|
||||
{
|
||||
AllZone.InputControl.setInput(input_targetValid(ability, target.getValidTgts(), target.getVTSelection(), this, req, false));
|
||||
}
|
||||
boolean mandatoryTarget = target.getMandatory() ? target.hasCandidates() : false;
|
||||
|
||||
AllZone.InputControl.setInput(input_targetValid(ability, target.getValidTgts(), target.getVTSelection(), this, req, mandatoryTarget));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -100,6 +97,12 @@ public class Target_Selection {
|
||||
Target tgt = select.getTgt();
|
||||
String zone = tgt.getZone();
|
||||
|
||||
if (zone.equals(Constant.Zone.Stack)){
|
||||
// If Zone is Stack, the choices are handled slightly differently
|
||||
stopSetNext(input_cardFromStack(sa, message, select, req, mandatory));
|
||||
return;
|
||||
}
|
||||
|
||||
CardList choices = AllZoneUtil.getCardsInZone(zone).getValidCards(Tgts, sa.getActivatingPlayer(), sa.getSourceCard());
|
||||
|
||||
// Remove cards already targeted
|
||||
@@ -120,10 +123,10 @@ public class Target_Selection {
|
||||
canTargetOpponent = true;
|
||||
}
|
||||
|
||||
stopSetNext(input_targetSpecific(sa, choices, message, true, canTargetPlayer, canTargetOpponent, select, req,mandatory));
|
||||
stopSetNext(input_targetSpecific(sa, choices, message, true, canTargetPlayer, canTargetOpponent, select, req, mandatory));
|
||||
}
|
||||
else{
|
||||
stopSetNext(input_cardFromList(sa, choices, message, true, select, req,mandatory));
|
||||
stopSetNext(input_cardFromList(sa, choices, message, true, select, req, mandatory));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -245,5 +248,140 @@ public class Target_Selection {
|
||||
};//Input
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
public static Input input_cardFromStack(final SpellAbility sa, final String message,
|
||||
final Target_Selection select, final SpellAbility_Requirements req, final boolean mandatory){
|
||||
|
||||
Input targetOnStack = new Input() {
|
||||
private static final long serialVersionUID = 5360660530175041997L;
|
||||
|
||||
@Override
|
||||
public void showMessage() {
|
||||
// Find what's targetable, then allow human to choose
|
||||
ArrayList<SpellAbility> choosables = getTargetableOnStack(sa, select.getTgt());
|
||||
|
||||
HashMap<String,SpellAbility> map = new HashMap<String,SpellAbility>();
|
||||
|
||||
for(SpellAbility sa : choosables) {
|
||||
map.put(sa.getStackDescription(),sa);
|
||||
}
|
||||
|
||||
String[] choices = new String[map.keySet().size()];
|
||||
choices = map.keySet().toArray(choices);
|
||||
|
||||
if (choices.length == 0){
|
||||
select.setCancel(true);
|
||||
}
|
||||
else{
|
||||
String madeChoice = GuiUtils.getChoiceOptional(message, choices);
|
||||
|
||||
if (madeChoice != null){
|
||||
Target tgt = select.getTgt();
|
||||
tgt.addTarget(map.get(madeChoice));
|
||||
}
|
||||
else
|
||||
select.setCancel(true);
|
||||
}
|
||||
|
||||
stop();
|
||||
req.finishedTargeting();
|
||||
}//showMessage()
|
||||
};
|
||||
return targetOnStack;
|
||||
}
|
||||
|
||||
// TODO: The following three functions are Utility functions for TargetOnStack, probably should be moved
|
||||
// The following should be select.getTargetableOnStack()
|
||||
public static ArrayList<SpellAbility> getTargetableOnStack(SpellAbility sa, Target tgt){
|
||||
ArrayList<SpellAbility> choosables = new ArrayList<SpellAbility>();
|
||||
|
||||
for(int i = 0; i < AllZone.Stack.size(); i++) {
|
||||
choosables.add(AllZone.Stack.peek(i));
|
||||
}
|
||||
|
||||
for(int i = 0; i < choosables.size(); i++) {
|
||||
if (!matchSpellAbility(sa, choosables.get(i), tgt)){
|
||||
choosables.remove(i);
|
||||
}
|
||||
}
|
||||
return choosables;
|
||||
}
|
||||
|
||||
public static boolean matchSpellAbility(SpellAbility sa, SpellAbility match, Target tgt){
|
||||
String saType = tgt.getTargetSpellAbilityType();
|
||||
|
||||
if (match.isSpell()){
|
||||
if (!saType.contains("Spell"))
|
||||
return false;
|
||||
}
|
||||
else if (match.isTrigger()){
|
||||
if (!saType.contains("Triggered"))
|
||||
return false;
|
||||
}
|
||||
else if (match.isAbility()){
|
||||
if (!saType.contains("Activated"))
|
||||
return false;
|
||||
}
|
||||
|
||||
String splitTargetRestrictions = tgt.getSAValidTargeting();
|
||||
if(splitTargetRestrictions != null){
|
||||
// TODO: What about spells with SubAbilities with Targets?
|
||||
|
||||
Target matchTgt = match.getTarget();
|
||||
|
||||
if (matchTgt == null)
|
||||
return false;
|
||||
|
||||
boolean result = false;
|
||||
|
||||
for(Object o : matchTgt.getTargets()){
|
||||
if(matchesValid(o, splitTargetRestrictions.split(","), sa)){
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!matchesValid(match, tgt.getValidTgts(), sa)){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean matchesValid(Object o, String[] valids, SpellAbility sa)
|
||||
{
|
||||
Card srcCard = sa.getSourceCard();
|
||||
Player activatingPlayer = sa.getActivatingPlayer();
|
||||
if(o instanceof Card){
|
||||
Card c = (Card)o;
|
||||
return c.isValidCard(valids, activatingPlayer, srcCard);
|
||||
}
|
||||
|
||||
if(o instanceof Player){
|
||||
for(String v : valids){
|
||||
if(v.equalsIgnoreCase("Player"))
|
||||
return true;
|
||||
|
||||
if(v.equalsIgnoreCase("Opponent")){
|
||||
if(o.equals(activatingPlayer.getOpponent())){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(v.equalsIgnoreCase("You"))
|
||||
return o.equals(activatingPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
if (o instanceof SpellAbility){
|
||||
Card c = ((SpellAbility)o).getSourceCard();
|
||||
return c.isValidCard(valids, activatingPlayer, srcCard);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user