1) Added Valakut, the Molten Pinnacle, and Expedition Map.

2) Tweaked the land-searching functions to reuse code for Expedition Map.

3) Initial fix for the human player for Cascade and Burst Lightning. A larger-scope fix including all Kicker cards, Buyback cards, and the AI will be added later.
This commit is contained in:
jendave
2011-08-06 04:32:36 +00:00
parent f7c7a2c4e1
commit 3127e3190b
8 changed files with 275 additions and 28 deletions

View File

@@ -38,6 +38,8 @@ snow_covered_mountain.jpg http://www.wizards.com/global/images/magic/gene
snow_covered_mountain1.jpg http://www.wizards.com/global/images/magic/general/snow_covered_mountain.jpg
snow_covered_mountain2.jpg http://www.magickartenmarkt.de/img/cards/Ice_Age/snow_covered_mountain.jpg
snow_covered_mountain3.jpg http://www.magickartenmarkt.de/img/cards/Ice_Age/snow_covered_mountain.jpg
expedition_map.jpg http://www.wizards.com/global/images/magic/general/expedition_map.jpg
valakut_the_molten_pinnacle.jpg http://www.wizards.com/global/images/magic/general/valakut_the_molten_pinnacle.jpg
power_matrix.jpg http://www.wizards.com/global/images/magic/general/power_matrix.jpg
scornful_aether_lich.jpg http://www.wizards.com/global/images/magic/general/scornful_aether_lich.jpg
recall.jpg http://www.wizards.com/global/images/magic/general/recall.jpg

View File

@@ -1,3 +1,15 @@
Expedition Map
1
Artifact
no text
Valakut, the Molten Pinnacle
no cost
Land
Whenever a Mountain enters the battlefield under your control, if you control at least five other Mountains, you may have Valakut, the Molten Pinnacle deal 3 damage to target creature or player.
Valakut, the Molten Pinnacle enters the battlefield tapped.
tap: add R
Serpent of the Endless Sea
4 U
Creature Serpent

View File

@@ -291,6 +291,18 @@ public class AllZoneUtil {
return cards;
}
/**
* gets a list of all cards of a certain type that a given player has in his library
* @param player the player to check for cards in play
* @param cardType the card type to check for
* @return a CardList with all cards of a certain type the player has in his library
*/
public static CardList getPlayerTypeInLibrary(final String player, final String cardType) {
CardList cards = getPlayerCardsInLibrary(player);
cards = cards.getType(cardType);
return cards;
}
////////////// cardListFilter for different types
public static CardListFilter artifacts = new CardListFilter() {
public boolean addCard(Card c) {

View File

@@ -1,6 +1,6 @@
package forge;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -18121,8 +18121,6 @@ public class CardFactory implements NewConstants {
@Override
public void resolve() {
PlayerZone grave = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
PlayerZone removed = AllZone.getZone(Constant.Zone.Removed_From_Play, card.getController());
if(getTargetCard() != null) {
if(AllZone.GameAction.isCardInPlay(getTargetCard())
@@ -18132,12 +18130,12 @@ public class CardFactory implements NewConstants {
}
} else AllZone.GameAction.getPlayerLife(getTargetPlayer()).subtractLife(damage);
grave.remove(card);
removed.add(card);
card.setKicked(true);
}
};//flashback
kicker.setManaCost("4 R");
kicker.setManaCost("R 4");
kicker.setAdditionalManaCost("4");
kicker.setKickerAbility(true);
kicker.setBeforePayMana(CardFactoryUtil.input_targetCreaturePlayer(kicker, true, false));
kicker.setDescription("Kicker: 4");
@@ -19840,6 +19838,31 @@ public class CardFactory implements NewConstants {
};//Input
spell.setBeforePayMana(target);
}//*************** END ************ END **************************
//*************** START *********** START **************************
else if(cardName.equals("Expedition Map")) {
final Ability_Tap ability = new Ability_Tap(card, "2") {
private static final long serialVersionUID = -5796728507926918991L;
@Override
public boolean canPlayAI() {
return AllZoneUtil.getPlayerTypeInLibrary(Constant.Player.Computer,
"Land").size() >= 1;
}
@Override
public void resolve() {
AllZone.GameAction.searchLibraryLand("Land",
card.getController(), Constant.Zone.Hand, false);
AllZone.GameAction.sacrifice(card);
}
};//ability
ability.setDescription("2, tap, sacrifice Expedition Map: Search your library for a land card, reveal it, and put it into your hand. Then shuffle your library.");
ability.setStackDescription("Sacrifice Expedition Map: search your library for a land and put it into your hand.");
ability.setManaCost("2");
card.addSpellAbility(ability);
}//*************** END ************ END **************************
//*************** START *********** START **************************
else if(cardName.equals("Recall")) {

View File

@@ -5,6 +5,7 @@ package forge;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
@@ -1415,7 +1416,7 @@ public class GameAction {
playSpellAbilityForFree(sa);
}
public void playSpellAbilityForFree(SpellAbility sa) {
public void playSpellAbilityForFree(final SpellAbility sa) {
if(sa.getBeforePayMana() == null) {
@@ -1423,6 +1424,17 @@ public class GameAction {
if (sa.getSourceCard().getManaCost().contains("X"))
x = true;
if (sa.isKickerAbility()) {
Command paid1 = new Command() {
private static final long serialVersionUID = 1L;
public void execute() {
AllZone.Stack.add(sa);
}
};
AllZone.InputControl.setInput(new Input_PayManaCost_Ability(sa.getAdditionalManaCost(),paid1));
}
AllZone.Stack.add(sa, x);
/*
@@ -1435,7 +1447,12 @@ public class GameAction {
}
*/
} else {
sa.getBeforePayMana().setFree(true);
if (sa.isKickerAbility()) {
sa.getBeforePayMana().setFree(false);
sa.setManaCost(sa.getAdditionalManaCost());
} else {
sa.getBeforePayMana().setFree(true);
}
AllZone.InputControl.setInput(sa.getBeforePayMana());
}
}
@@ -1718,31 +1735,70 @@ public class GameAction {
}
public void searchLibraryBasicLand(String player, String Zone1, boolean tapLand) {
searchLibraryTwoBasicLand(player, Zone1, tapLand, "", false);
public void searchLibraryLand(String type, String player, String Zone1, boolean tapLand) {
searchLibraryTwoLand(type, player, Zone1, tapLand, "", false);
}
public void searchLibraryTwoBasicLand(String player,
String Zone1, boolean tapFirstLand,
String Zone2, boolean tapSecondLand) {
public void searchLibraryBasicLand(String player, String Zone1, boolean tapLand) {
searchLibraryTwoLand("Basic", player, Zone1, tapLand, "", false);
}
public void searchLibraryTwoLand(String type, String player,
String Zone1, boolean tapFirstLand,
String Zone2, boolean tapSecondLand) {
if(player.equals(Constant.Player.Human)) {
humanSearchTwoBasicLand(Zone1, tapFirstLand, Zone2, tapSecondLand);
humanSearchTwoLand(type, Zone1, tapFirstLand, Zone2, tapSecondLand);
} else {
aiSearchTwoBasicLand(Zone1, tapFirstLand, Zone2, tapSecondLand);
aiSearchTwoLand(type, Zone1, tapFirstLand, Zone2, tapSecondLand);
}
AllZone.GameAction.shuffle(player);
}
public void searchLibraryTwoBasicLand(String player,
String Zone1, boolean tapFirstLand,
String Zone2, boolean tapSecondLand) {
searchLibraryTwoLand("Basic", player, Zone1, tapFirstLand, Zone2, tapSecondLand);
}
private void aiSearchTwoBasicLand(String Zone1, boolean tapFirstLand,
private void aiSearchTwoLand(String type, String Zone1, boolean tapFirstLand,
String Zone2, boolean tapSecondLand) {
CardList land = new CardList(AllZone.Computer_Library.getCards());
land = land.getType("Basic");
land = land.getType(type);
PlayerZone firstZone = AllZone.getZone(Zone1, Constant.Player.Computer);
//just to make the computer a little less predictable
land.shuffle();
if (type.contains("Basic")) {
// No need for special sorting for basic land
// just shuffle to make the computer a little less predictable
land.shuffle();
} else {
Comparator<Card> aiLandComparator = new Comparator<Card>()
{
private int scoreLand(Card a) {
String valakutName = "Valakut, the Molten Pinnacle";
int theScore = 0;
if (!a.isBasicLand()) {
// favor non-basic land
theScore++;
if (a.getName().contains(valakutName)) {
// TODO: Add names of other special lands
theScore++;
}
}
return theScore;
}
public int compare(Card a, Card b)
{
int aScore = scoreLand(a);
int bScore = scoreLand(b);
return bScore - aScore;
} // compare
};//Comparator
// Prioritize the land somewhat
land.sort(aiLandComparator);
}
//3 branches: 1-no land in deck, 2-one land in deck, 3-two or more land in deck
if(land.size() != 0) {
//branch 2 - at least 1 land in library
@@ -1765,20 +1821,28 @@ public class GameAction {
}
}
private void humanSearchTwoBasicLand(String Zone1, boolean tapFirstLand, String Zone2, boolean tapSecondLand) {
private void humanSearchTwoLand(String type, String Zone1, boolean tapFirstLand, String Zone2, boolean tapSecondLand) {
PlayerZone firstZone = AllZone.getZone(Zone1, Constant.Player.Human);
PlayerZone library = AllZone.getZone(Constant.Zone.Library, Constant.Player.Human);
CardList list = new CardList(library.getCards());
list = list.getType("Basic");
list = list.getType(type);
//3 branches: 1-no land in deck, 2-one land in deck, 3-two or more land in deck
//branch 1
if(list.size() == 0) return;
// Check whether we were only asked for one land, and adjust the prompt accordingly
boolean onlyOneLand = (Zone2.trim().length() == 0);
String firstPrompt;
if (onlyOneLand)
firstPrompt = new String("Choose a land");
else
firstPrompt = new String("Choose first land");
//branch 2
Object o = AllZone.Display.getChoiceOptional("Choose first land", list.toArray());
Object o = AllZone.Display.getChoiceOptional(firstPrompt, list.toArray());
if(o != null) {
Card c = (Card) o;
list.remove(c);
@@ -1789,7 +1853,7 @@ public class GameAction {
firstZone.add(c);
}//if
if ((list.size() == 0) || Zone2.trim().length() == 0) return;
if ((list.size() == 0) || onlyOneLand) return;
//branch 3
o = AllZone.Display.getChoiceOptional("Choose second land", list.toArray());
if(o != null) {

View File

@@ -3742,6 +3742,118 @@ public class GameActionUtil {
else if(c.getName().equals("Bloodghast")) landfall_Bloodghast(c);
else if(c.getName().equals("Avenger of Zendikar")) landfall_Avenger_of_Zendikar(c);
}
private static boolean checkValakutCondition(Card valakutCard, Card mtn) {
// Get a list of all mountains
CardList mountainList = AllZoneUtil.getPlayerTypeInPlay(valakutCard.getController(),
"Mountain");
// Don't count the one that just came into play
if (mountainList.contains(mtn))
mountainList.remove(mtn);
// Do not activate if at least 5 other mountains are not present.
if (mountainList.size() < 5)
return false;
else
return true;
}
// Returns true if the routine found enough mountains to activate the effect
// Returns false otherwise
// This lets the calling routine break if a player has multiple Valakut in play
public static boolean executeValakutEffect(final Card valakutCard, final Card mtn) {
if (!checkValakutCondition(valakutCard, mtn))
return false; // Tell the calling routine there aren't enough mountains, don't call again
SpellAbility DamageTgt = new Spell(valakutCard) {
private static final long serialVersionUID = -7360567876931046530L;
public boolean canPlayAI() {
return getCreature().size() != 0 || AllZone.Human_Life.getLife() < 10;
}
public boolean canPlay() {
return true;
}
CardList getCreature() {
//toughness of 3
CardList list = CardFactoryUtil.AI_getHumanCreature(3, valakutCard, true);
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
//only get 1/1 flyers or 2/1 or bigger creatures
return (2 <= c.getNetAttack()) || c.getKeyword().contains("Flying");
}
});
return list;
}//getCreature()
@Override
public void chooseTargetAI() {
boolean targetHuman;
// Get a list of all creatures Valakut could destroy
CardList list = getCreature();
CardList listValakut = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getName().contains("Valakut, the Molten Pinnacle");
}
});
int lifeThreshold = Math.max( 3 * listValakut.size(), 6);
if ( (AllZone.Human_Life.getLife() < lifeThreshold) || list.isEmpty()) {
targetHuman = true;
} else {
// Remove any creatures that have been targeted by other Valakuts
for (int ix = 0; ix < AllZone.Stack.size(); ix++) {
SpellAbility sa = AllZone.Stack.peek(ix);
if (sa.getSourceCard().getName().contains("Valakut, the Molten Pinnacle")) {
Card target = sa.getTargetCard();
if ((target != null) && list.contains(target)) {
list.remove(target);
}
}
}
if (list.isEmpty()) {
targetHuman = true;
} else {
targetHuman = false;
}
}
if(targetHuman) setTargetPlayer(Constant.Player.Human);
else {
list.shuffle();
setTargetCard(list.get(0));
}
}//chooseTargetAI()
@Override
public void resolve() {
if (!checkValakutCondition(valakutCard, mtn))
return;
if(getTargetCard() != null) {
if(AllZone.GameAction.isCardInPlay(getTargetCard())
&& CardFactoryUtil.canTarget(valakutCard, getTargetCard())) getTargetCard().addDamage(3,
valakutCard);
} else AllZone.GameAction.getPlayerLife(getTargetPlayer()).subtractLife(3);
}//resolve()
};
DamageTgt.setManaCost(new String("0"));
DamageTgt.setStackDescription("Valakut, the Molten Pinnacle deals 3 damage to target creature or player.");
if (valakutCard.getController() == Constant.Player.Human) {
AllZone.InputControl.setInput(CardFactoryUtil.input_targetCreaturePlayer(DamageTgt, true, true));
} else {
DamageTgt.chooseTargetAI();
AllZone.Stack.add(DamageTgt);
}
return true; // Tell the calling routine it's okay to call again if there are other Valakuts in play
}
private static boolean showLandfallDialog(Card c) {
String[] choices = {"Yes", "No"};

View File

@@ -75,6 +75,12 @@ public class PlayerZone_ComesIntoPlay extends DefaultPlayerZone {
CardList list = new CardList(play.getCards());
CardList graveList = new CardList(grave.getCards());
CardList listValakut = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getName().contains("Valakut, the Molten Pinnacle");
}
});
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getKeyword().contains("Landfall") ||
@@ -96,6 +102,16 @@ public class PlayerZone_ComesIntoPlay extends DefaultPlayerZone {
GameActionUtil.executeLandfallEffects(list.get(i));
}
// Check for a mountain
if (!listValakut.isEmpty() && c.getType().contains("Mountain") ) {
for (int i = 0; i < listValakut.size(); i++) {
boolean b = GameActionUtil.executeValakutEffect(listValakut.get(i),c);
if (!b) {
// Not enough mountains to activate Valakut -- stop the loop
break;
}
}
}
}//isLand()
//hack to make tokens trigger ally effects:

View File

@@ -31,6 +31,7 @@ public abstract class SpellAbility {
private boolean flashBackAbility = false;
private boolean multiKicker = false;
private boolean xCost = false;
private boolean kickerAbility = false;
private Input beforePayMana;
private Input afterResolve;
@@ -296,7 +297,12 @@ public abstract class SpellAbility {
public boolean isFlashBackAbility() {
return flashBackAbility;
}
public void setKickerAbility(boolean kab) {
this.kickerAbility=kab;
}
public boolean isKickerAbility() {
return kickerAbility;
}
// Only used by Ability_Reflected_Mana, because the user has an option to cancel the input.
// Most spell abilities and even most mana abilities do not need to use this.
public boolean wasCancelled() {