Moved reflected mana ability code into a new class. Made reflected lands playable from Input_PayManaCost.

This commit is contained in:
jendave
2011-08-06 03:55:14 +00:00
parent ba0070ddda
commit ec29437640
10 changed files with 283 additions and 247 deletions

1
.gitattributes vendored
View File

@@ -87,6 +87,7 @@ src/forge/Ability.java svneol=native#text/plain
src/forge/Ability_Activated.java svneol=native#text/plain
src/forge/Ability_Hand.java svneol=native#text/plain
src/forge/Ability_Mana.java -text svneol=native#text/plain
src/forge/Ability_Reflected_Mana.java svneol=native#text/plain
src/forge/Ability_Tap.java svneol=native#text/plain
src/forge/Ability_Triggered.java svneol=native#text/plain
src/forge/All.java svneol=native#text/plain

View File

@@ -4,7 +4,6 @@ package forge;
import java.util.ArrayList;
abstract public class Ability_Mana extends SpellAbility implements java.io.Serializable {
private ArrayList<Command> runcommands = new ArrayList<Command>();
public String orig;
@@ -38,16 +37,6 @@ abstract public class Ability_Mana extends SpellAbility implements java.io.Seria
return isTapAbility(orig);
}
// This is a work-in-progress *coughcough HACK coughcough*
// This should return the list of all possible colors for the mana ability
// Instead, right now it only does so for reflected lands, which override
// this ability.
// For any other kind of land, the method asserts. If you find you need to call
// this method, implement it...
public ArrayList<String> getPossibleColors() {
throw new RuntimeException("Ability_Mana : getPossibleColors() not implemented");
}
private static boolean isTapAbility(String orig) {
String cost = orig.split(":")[0];
cost = cost.replaceAll("Tap", "tap").replaceAll("tap", "T");
@@ -279,4 +268,7 @@ abstract public class Ability_Mana extends SpellAbility implements java.io.Seria
}
return false;
}
}

View File

@@ -0,0 +1,224 @@
package forge;
import java.util.ArrayList;
public class Ability_Reflected_Mana extends Ability_Mana {
private static final long serialVersionUID = 7050614528410938233L;
String colorChosen;
String colorOrType;
String who;
boolean choiceWasMade = false;
public Ability_Reflected_Mana(final Card card, String abString, String colorOrType, String who) {
super(card, abString);
this.colorOrType = new String(colorOrType);
this.who = new String(who);
this.setReflectedMana(true);
this.colorChosen = new String("0"); // Default -- add no mana
this.undoable = true;
}
public String getTargetPlayer() {
String targetPlayer;
if (this.who.startsWith("Opp")) {
targetPlayer = AllZone.GameAction.getOpponent(this.getSourceCard().getController());
} else {
targetPlayer = this.getSourceCard().getController();
}
return targetPlayer;
}
public void undo() {
this.reset();
super.undo();
}
public ArrayList<String> getPossibleColors() {
String targetPlayer;
if (this.who.startsWith("Opp")) {
targetPlayer = AllZone.GameAction.getOpponent(this.getSourceCard().getController());
} else {
targetPlayer = this.getSourceCard().getController();
}
ArrayList<String> possibleColors = getManaProduceList(targetPlayer, this.colorOrType);
return possibleColors;
}
public boolean canPlay() {
ArrayList<String> possibleColors = this.getPossibleColors();
if (possibleColors.isEmpty()) {
// Can't use these cards if there are no mana-producing lands in play
return false;
} else {
return super.canPlay();
}
}
public void resolve() {
if (!this.choiceWasMade)
this.chooseManaColor();
if (this.choiceWasMade)
super.resolve();
}
public String mana() {
return this.colorChosen;
}
public void reset() {
this.colorChosen = "0";
this.choiceWasMade = false;
}
public boolean wasCancelled() {
return !this.choiceWasMade;
}
public void chooseManaColor() {
ArrayList<String> possibleColors = this.getPossibleColors();
if (possibleColors.isEmpty()) {
// No mana available: card doesn't tap and nothing happens
this.colorChosen = "0";
} else if (possibleColors.size() == 1) {
// Card taps for the only mana available
this.colorChosen =
Input_PayManaCostUtil.getShortColorString(possibleColors.get(0));
this.choiceWasMade = true;
}
else {
// Choose a color of mana to produce.
Object o = AllZone.Display.getChoiceOptional("Select Mana to Produce", possibleColors.toArray());
if (o == null) {
// User hit cancel
this.colorChosen = "0";
this.choiceWasMade = false;
} else {
this.colorChosen =
Input_PayManaCostUtil.getShortColorString((String) o);
this.choiceWasMade = true;
}
}
}
// Return the list of mana types or colors that the target player's land can produce
// This is used by the mana abilities created by the abReflectedMana keyword
public static ArrayList<String> getManaProduceList(String player, String colorOrType) {
ArrayList<String> colorsPlayerCanProduce = new ArrayList<String>();
ArrayList<String> colorsToLookFor = new ArrayList<String>();
if (colorOrType.startsWith("Type")) {
// Includes colorless (like Reflecting Pool)
for (int ic = 0; ic < Constant.Color.Colors.length; ic++) {
colorsToLookFor.add(Constant.Color.Colors[ic]);
}
} else {
// Excludes colorless (like Exotic Orchard)
for (int ic = 0; ic < Constant.Color.onlyColors.length; ic++) {
colorsToLookFor.add(Constant.Color.onlyColors[ic]);
}
}
// Build the list of cards to search for mana colors
// First, add all the cards owned by the target player
CardList cl = new CardList();
cl.addAll(AllZone.getZone(Constant.Zone.Play,player).getCards());
// Narrow down the card list to only non-reflected lands
// If during this search we find another reflected land, and it targets a different player
// than this land, then we have to search that player's lands as well
boolean addOtherPlayerLands = false;
int ix = 0;
while (ix < cl.size()) {
Card otherCard = cl.get(ix);
if (otherCard.isLand()) {
if (otherCard.isReflectedLand() && !addOtherPlayerLands) {
ArrayList<Ability_Mana> amList = otherCard.getManaAbility();
// We assume reflected lands have only one mana ability
// Find out which player it targets
Ability_Mana am = amList.get(0);
String otherTargetPlayer = am.getTargetPlayer();
// If the target player of the other land isn't the same as the target player
// of this land, we need to search the sets of mana he can produce as well.
if (!otherTargetPlayer.equals(player)) {
addOtherPlayerLands = true; // We only need to record this decision once
}
// Don't keep reflected lands in the list of lands
cl.remove(ix);
} else {
// Other card is a land but not a reflected land
ix++; // leave in list & look at next card
}
} else {
// Other card is not a land -- remove it
cl.remove(ix);
}
} // while ix < cl.size
getManaFromCardList(cl, colorsPlayerCanProduce, colorsToLookFor);
if (addOtherPlayerLands) {
cl.clear();
cl.addAll(AllZone.getZone(Constant.Zone.Play,AllZone.GameAction.getOpponent(player)).getCards());
cl = cl.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.isLand() && !c.isReflectedLand();
}
});
// Exotic Orchard, which is the only way to get colors from another
// player's lands, looks for colors. Therefore, we should not look
// through another player's lands for colorless mana. This is true
// even if the original card happens to have been a reflecting pool.
if (colorsToLookFor.contains(Constant.Color.Colorless)) {
colorsToLookFor.remove(Constant.Color.Colorless);
}
if (!colorsToLookFor.isEmpty()) {
getManaFromCardList(cl, colorsPlayerCanProduce, colorsToLookFor);
}
}
return colorsPlayerCanProduce;
}
private static void getManaFromCardList(CardList cl, ArrayList<String> colorsPlayerCanProduce, ArrayList<String>colorsToLookFor) {
int ix;
// In this routine, the list cl must be a list of lands that are not reflected lands
// Otherwise if both players had Exotic Orchards we might keep searching
// their lands forever.
for (ix = 0; ix < cl.size(); ix++) {
Card otherCard = cl.get(ix);
ArrayList<Ability_Mana> amList = otherCard.getManaAbility();
for (int im = 0; im < amList.size(); im++) {
// Search all the mana abilities and add colors of mana
Ability_Mana am = amList.get(im);
String newMana = am.mana(); // This call would break for a reflected mana ability
int ic = 0;
// Check if any of the remaining colors are in this mana ability
while (ic < colorsToLookFor.size()) {
if (newMana.contains(Input_PayManaCostUtil.getShortColorString(colorsToLookFor.get(ic)))) {
colorsPlayerCanProduce.add(colorsToLookFor.remove(ic));
continue; // Don't increment index -- list got smaller
}
ic++; // Only increment if nothing was found
}
// If the search list is empty stop
if (colorsToLookFor.isEmpty()) {
break; // No point in continuing
}
} // Loop over mana abilities
if (colorsToLookFor.isEmpty()) {
break;
}
} // loop over list of lands
}
} // end of Ability_Reflected_Mana

View File

@@ -1304,6 +1304,10 @@ public class Card extends MyObservable {
}
public void untap() {
if (isTapped() && isReflectedLand()) {
Ability_Reflected_Mana am = (Ability_Reflected_Mana) getManaAbility().get(0);
am.reset();
}
setTapped(false);
}

View File

@@ -1637,12 +1637,7 @@ public class CardFactoryUtil {
return onLeavesPlay;
}//enPump_LeavesPlay
public static Ability_Mana getReflectedManaAbility(final Card card, String colorOrType, String who) {
class ReflectedManaInfo {
String colorChosen;
String colorOrType;
String who;
};
public static Ability_Reflected_Mana getReflectedManaAbility(final Card card, String colorOrType, String who) {
String whoString;
if (who.startsWith("Opp")) {
@@ -1652,115 +1647,13 @@ public class CardFactoryUtil {
String abString = "tap: add to your mana pool one mana of any " + colorOrType.toLowerCase() +
" that a land " + whoString + " could produce.";
final Ability_Mana theAbility = new Ability_Mana(card, abString) {
private static final long serialVersionUID = 1839038296416458319L;
@Override
public void undo() {
card.untap();
}
@Override
public String getTargetPlayer() {
ReflectedManaInfo rfi = (ReflectedManaInfo) this.choices_made[0];
String targetPlayer;
if (rfi.who.startsWith("Opp")) {
targetPlayer = AllZone.GameAction.getOpponent(card.getController());
} else {
targetPlayer = card.getController();
}
return targetPlayer;
}
@Override
public void resolve() {
// Only tap the card if it was not canceled
ReflectedManaInfo rfi = (ReflectedManaInfo) this.choices_made[0];
if (rfi.colorChosen != "0") {
card.tap();
super.resolve();
}
}
@Override
public String mana() {
ReflectedManaInfo rfi = (ReflectedManaInfo) this.choices_made[0];
return rfi.colorChosen;
}
@Override
public ArrayList<String> getPossibleColors() {
ReflectedManaInfo rfi = (ReflectedManaInfo) this.choices_made[0];
String targetPlayer;
if (rfi.who.startsWith("Opp")) {
targetPlayer = AllZone.GameAction.getOpponent(card.getController());
} else {
targetPlayer = card.getController();
}
ArrayList<String> possibleColors = GameActionUtil.getManaProduceList(targetPlayer, rfi.colorOrType);
return possibleColors;
}
@Override
public boolean canPlay() {
ArrayList<String> possibleColors = this.getPossibleColors();
if (possibleColors.isEmpty()) {
// Can't use these cards if there are no mana-producing lands in play
return false;
} else {
//if(choices_made[0] == null) choices_made[0] = "0";
return super.canPlay();
}
}
};
theAbility.undoable=true;
//theAbility.choices_made = new String[1];
theAbility.choices_made = new ReflectedManaInfo[1];
ReflectedManaInfo rfi = new ReflectedManaInfo();
rfi.colorChosen = new String("0");
rfi.colorOrType = new String(colorOrType);
rfi.who = new String(who);
theAbility.choices_made[0] = rfi;
Ability_Reflected_Mana theAbility = new Ability_Reflected_Mana(card, abString, colorOrType, who);
return theAbility;
//((ReflectedManaInfo)theAbility.choices_made[0]).colorChosen = new String("0");
//((ReflectedManaInfo)theAbility.choices_made[0]).colorOrType = new String(colorOrType);
//((ReflectedManaInfo)theAbility.choices_made[0]).who = new String(who);
theAbility.setReflectedMana(true);
theAbility.setBeforePayMana(new Input() {
private static final long serialVersionUID = -2106126894846529731L;
@Override
public void showMessage() {
ArrayList<String> possibleColors = theAbility.getPossibleColors();
if (possibleColors.isEmpty()) {
// No mana available: card doesn't tap and nothing happens
((ReflectedManaInfo)theAbility.choices_made[0]).colorChosen = "0";
} else if (possibleColors.size() == 1) {
// Card taps for the only mana available
// If there's only one choice, we're not giving the option to cancel.
((ReflectedManaInfo)theAbility.choices_made[0]).colorChosen =
Input_PayManaCostUtil.getShortColorString(possibleColors.get(0));
}
else {
// Choose a color of mana to produce. If cancel is chosen, no mana is produced and the
// card doesn't tap.
Object o = AllZone.Display.getChoiceOptional("Select a Color of Mana to Produce", possibleColors.toArray());
if (o == null) {
((ReflectedManaInfo)theAbility.choices_made[0]).colorChosen = "0";
} else {
((ReflectedManaInfo)theAbility.choices_made[0]).colorChosen =
Input_PayManaCostUtil.getShortColorString((String) o);
}
}
AllZone.Stack.add(theAbility);
stop();
}
});
return theAbility;
} // End getReflectedManaAbility
public static SpellAbility enPumpCurse_Enchant(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords,

View File

@@ -284,8 +284,10 @@ public class ComputerUtil
{
ArrayList<String> colors = new ArrayList<String>();
if (land.isReflectedLand()){
// Reflected lands (Exotic Orchard and Reflecting Pool) have one
// mana ability, and it has a method called 'getPossibleColors"
ArrayList<Ability_Mana> amList = land.getManaAbility();
colors = amList.get(0).getPossibleColors();
colors = ((Ability_Reflected_Mana)amList.get(0)).getPossibleColors();
} else {
if (land.getKeyword().contains("tap: add B"))
colors.add(Constant.Color.Black);

View File

@@ -1300,7 +1300,7 @@ public class GameAction {
public void playSpellAbility(SpellAbility sa) {
if(sa.getManaCost().equals("0") && sa.getBeforePayMana() == null) {
AllZone.Stack.add(sa);
if(sa.isTapAbility()) sa.getSourceCard().tap();
if(sa.isTapAbility() && !sa.wasCancelled()) sa.getSourceCard().tap();
if(sa.isUntapAbility()) sa.getSourceCard().untap();
return;
}

View File

@@ -2688,121 +2688,6 @@ public class GameActionUtil {
//END ENDOFTURN CARDS
// Return the list of mana types or colors that the target player's land can produce
// This is used by the mana abilities created by the abReflectedMana keyword
public static ArrayList<String> getManaProduceList(String player, String colorOrType) {
ArrayList<String> colorsPlayerCanProduce = new ArrayList<String>();
ArrayList<String> colorsToLookFor = new ArrayList<String>();
if (colorOrType.startsWith("Type")) {
// Includes colorless (like Reflecting Pool)
for (int ic = 0; ic < Constant.Color.Colors.length; ic++) {
colorsToLookFor.add(Constant.Color.Colors[ic]);
}
} else {
// Excludes colorless (like Exotic Orchard)
for (int ic = 0; ic < Constant.Color.onlyColors.length; ic++) {
colorsToLookFor.add(Constant.Color.onlyColors[ic]);
}
}
// Build the list of cards to search for mana colors
// First, add all the cards owned by the target player
CardList cl = new CardList();
cl.addAll(AllZone.getZone(Constant.Zone.Play,player).getCards());
// Narrow down the card list to only non-reflected lands
// If during this search we find another reflected land, and it targets a different player
// than this land, then we have to search that player's lands as well
boolean addOtherPlayerLands = false;
int ix = 0;
while (ix < cl.size()) {
Card otherCard = cl.get(ix);
if (otherCard.isLand()) {
if (otherCard.isReflectedLand() && !addOtherPlayerLands) {
ArrayList<Ability_Mana> amList = otherCard.getManaAbility();
// We assume reflected lands have only one mana ability
// Find out which player it targets
Ability_Mana am = amList.get(0);
String otherTargetPlayer = am.getTargetPlayer();
// If the target player of the other land isn't the same as the target player
// of this land, we need to search the sets of mana he can produce as well.
if (!otherTargetPlayer.equals(player)) {
addOtherPlayerLands = true; // We only need to record this decision once
}
// Don't keep reflected lands in the list of lands
cl.remove(ix);
} else {
// Other card is a land but not a reflected land
ix++; // leave in list & look at next card
}
} else {
// Other card is not a land -- remove it
cl.remove(ix);
}
} // while ix < cl.size
GameActionUtil.getManaFromCardList(cl, colorsPlayerCanProduce, colorsToLookFor);
if (addOtherPlayerLands) {
cl.clear();
cl.addAll(AllZone.getZone(Constant.Zone.Play,AllZone.GameAction.getOpponent(player)).getCards());
cl.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.isLand() && !c.isReflectedLand();
}
});
// Exotic Orchard, which is the only way to get colors from another
// player's lands, looks for colors. Therefore, we should not look
// through another player's lands for colorless mana. This is true
// even if the original card happens to have been a reflecting pool.
if (colorsToLookFor.contains(Constant.Color.Colorless)) {
colorsToLookFor.remove(Constant.Color.Colorless);
}
if (!colorsToLookFor.isEmpty()) {
GameActionUtil.getManaFromCardList(cl, colorsPlayerCanProduce, colorsToLookFor);
}
}
return colorsPlayerCanProduce;
}
public static void getManaFromCardList(CardList cl, ArrayList<String> colorsPlayerCanProduce, ArrayList<String>colorsToLookFor) {
int ix;
// In this routine, the list cl must be a list of lands that are not reflected lands
// Otherwise if both players had Exotic Orchards we might keep searching
// their lands forever.
for (ix = 0; ix < cl.size(); ix++) {
Card otherCard = cl.get(ix);
ArrayList<Ability_Mana> amList = otherCard.getManaAbility();
for (int im = 0; im < amList.size(); im++) {
// Search all the mana abilities and add colors of mana
Ability_Mana am = amList.get(im);
String newMana = am.mana(); // This call would break for a reflected mana ability
int ic = 0;
// Check if any of the remaining colors are in this mana ability
while (ic < colorsToLookFor.size()) {
if (newMana.contains(Input_PayManaCostUtil.getShortColorString(colorsToLookFor.get(ic)))) {
colorsPlayerCanProduce.add(colorsToLookFor.remove(ic));
continue; // Don't increment index -- list got smaller
}
ic++; // Only increment if nothing was found
}
// If the search list is empty stop
if (colorsToLookFor.isEmpty()) {
break; // No point in continuing
}
} // Loop over mana abilities
if (colorsToLookFor.isEmpty()) {
break;
}
} // loop over list of lands
}
public static void removeAttackedBlockedThisTurn() {
// resets the status of attacked/blocked this turn
String player = AllZone.Phase.getActivePlayer();

View File

@@ -4,7 +4,7 @@ import java.util.*;
public class Input_PayManaCostUtil
{
//all mana abilities start with this and typical look like "tap: add G"
//mana abilities are Strings and are retreaved by calling card.getKeyword()
//mana abilities are Strings and are retrieved by calling card.getKeyword()
//taps any card that has mana ability, not just land
public static ManaCost tapCard(Card card, ManaCost manaCost)
{
@@ -15,7 +15,7 @@ public class Input_PayManaCostUtil
for(String color : Constant.Color.ManaColors)
if(manaCost.isNeeded(color))
cneeded.append(getShortColorString(color));
Iterator<Ability_Mana> it = abilities.iterator();//you can't remove unneded abilitie inside a for(am:abilities) loop :(
Iterator<Ability_Mana> it = abilities.iterator();//you can't remove unneeded abilities inside a for(am:abilities) loop :(
while(it.hasNext())
{
Ability_Mana ma = it.next();
@@ -28,17 +28,39 @@ public class Input_PayManaCostUtil
Ability_Mana chosen = abilities.get(0);
if(1 < abilities.size())
{
HashMap<String, Ability_Mana> ability = new HashMap<String, Ability_Mana>();
for(Ability_Mana am : abilities)
ability.put(am.toString(), am);
chosen = (Ability_Mana) AllZone.Display.getChoice("Choose mana ability", abilities.toArray());
HashMap<String, Ability_Mana> ability = new HashMap<String, Ability_Mana>();
for(Ability_Mana am : abilities)
ability.put(am.toString(), am);
chosen = (Ability_Mana) AllZone.Display.getChoice("Choose mana ability", abilities.toArray());
}
{
AllZone.GameAction.playSpellAbility(chosen);
{
if (chosen.isReflectedMana()) {
// Choose the mana color
Ability_Reflected_Mana arm = (Ability_Reflected_Mana) chosen;
arm.chooseManaColor();
// Only resolve if the choice wasn't cancelled and the mana was actually needed
if (arm.wasCancelled()) {
return manaCost;
} else {
String color = chosen.mana();
if (!manaCost.isNeeded(color)) {
// Don't tap the card if the user chose something invalid
arm.reset(); // Invalidate the choice
return manaCost;
}
}
// A valid choice was made -- resolve the ability and tap the card
arm.resolve();
arm.getSourceCard().tap();
} else {
AllZone.GameAction.playSpellAbility(chosen);
}
manaCost = AllZone.ManaPool.subtractMana(manaCost, chosen);
AllZone.Human_Play.updateObservers();//DO NOT REMOVE THIS, otherwise the cards don't always tap (copied)
return manaCost;
return manaCost;
}
}
public static ArrayList<Ability_Mana> getManaAbilities(Card card)
{
@@ -49,6 +71,14 @@ public class Input_PayManaCostUtil
{
if(mana.contains("1")) return true;
if(mana.contains("S") && am.isSnow()) return true;
if(am.isReflectedMana()) {
for( String color:((Ability_Reflected_Mana)am).getPossibleColors()) {
if (mana.contains(getShortColorString(color))) {
return true;
}
}
return false;
}
for(String color : ManaPool.getManaParts(am))
if(mana.contains(color)) return true;
return false;

View File

@@ -295,6 +295,11 @@ public abstract class SpellAbility {
return flashBackAbility;
}
// 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() {
return false;
}
public SpellAbility copy()
{
SpellAbility clone = null;