It is currently 07 Sep 2025, 23:43
   
Text Size

Card.getText()

Post MTG Forge Related Programming Questions Here

Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins

Card.getText()

Postby Chris H. » 12 Sep 2010, 13:05

I have recently spent some time in this section. We have a number of new keywords that were not showing up on Instants and Sorceries. I just added "CARDNAME is {color} for Ghostfire and Ancestral Vision. I also cleaned up and consolidated the code in this section.

I think that I am starting to understand why we have had this problem ever since Rob added the cantrip code. Instants and Sorceries have the spell abilities and the spellText combined but it had no code for adding keywords which do not create a spell ability with it's own spell ability description.

So things are a little bit better now but with the large number of new keywords added this summer it is possible that I may have overlooked a few. So the Card.getText() code for Instants and Sorceries now look like this:

Code: Select all
    public String getText() {
        if(isInstant() || isSorcery()) {
            String s = getSpellText();
            StringBuilder sb = new StringBuilder();
           
            // Give spellText line breaks for easier reading
            sb.append(s.replaceAll("\\\\r\\\\n", "\r\n"));
           
            // NOTE:
            if (sb.toString().contains(" (NOTE: ")) {
                sb.insert(sb.indexOf("(NOTE: "), "\r\n");
            }
            if (sb.toString().contains("(NOTE: ") && sb.toString().endsWith(".)") && !sb.toString().endsWith("\r\n")) {
                sb.append("\r\n");
            }
           
            // Add SpellAbilities
            SpellAbility[] sa = getSpellAbility();
            for (int i = 0; i < sa.length; i++) {
                sb.append(sa[i].toString() + "\r\n");
            }
           
            // Add Keywords
            ArrayList<String> kw = getKeyword();
           
            // Ripple + Dredge + Madness + CARDNAME is {color}.
            for (int i = 0; i < kw.size(); i++) {
                if ((kw.get(i).startsWith("Ripple") && !sb.toString().contains("Ripple"))
                        || (kw.get(i).startsWith("Dredge") && !sb.toString().contains("Dredge"))
                        || (kw.get(i).startsWith("Madness") && !sb.toString().contains("Madness"))
                        || (kw.get(i).startsWith("CARDNAME is ") && !sb.toString().contains("CARDNAME is "))) {
                    sb.append(kw.get(i).replace(":", " ")).append("\r\n");
                }
            }
           
            // Draw a card. + Changeling + CARDNAME can't be countered. + Cascade
            for (int i = 0; i < kw.size(); i++) {
                if ((kw.get(i).contains("Draw a card.") && !sb.toString().contains("Draw a card."))
                        || (kw.get(i).contains("Changeling") && !sb.toString().contains("Changeling"))
                        || (kw.get(i).contains("CARDNAME can't be countered.") && !sb.toString().contains("CARDNAME can't be countered."))
                        || (kw.get(i).contains("Cascade") && !sb.toString().contains("Cascade"))) {
                    sb.append(kw.get(i)).append("\r\n");
                }
            }
           
            // Storm
            if (getKeyword().contains("Storm") && !sb.toString().contains("Storm (When you ")) {
                if (sb.toString().endsWith("\r\n\r\n")) {
                    sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n")+3);
                }
                sb.append("Storm (When you cast this spell, copy it for each spell cast before it this turn.");
                if (sb.toString().contains("Target") || sb.toString().contains("target")) {
                    sb.append(" You may choose new targets for the copies.");
                }
                sb.append(")\r\n");
            }
           
            // Scry
            if(!sb.toString().contains("Scry")) for(int i = 0; i < getKeyword().size(); i++) {
                String k = getKeyword().get(i);
                if(k.startsWith("Scry")) {
                    String kk[] = k.split(" ");
                    //sb.append("Scry " + kk[1] + " (To scry X, look at the top X cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)\r\n");
                    sb.append("Scry ");
                    sb.append(kk[1]);
                    sb.append(" (To scry X, look at the top X cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.)\r\n");
                }
            }
           
            while (sb.toString().endsWith("\r\n")) {
                sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n")+3);
            }
           
            return sb.toString().replaceAll("CARDNAME", getName());
        }
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: Card.getText()

Postby Rob Cashwalker » 12 Sep 2010, 13:44

I'm sure this is on the top end of "best we can do now", but I also have a sneaking suspicion that it could be done cleaner, without including so much rules text that could be changed with any new set release when they revise oracle text.

Maybe we shouldn't try to use the spell descriptions for each spell ability. Instead, use the text field (now with line feeds!) for what it was intended. It's a field that the python script can maintain easily.
The spell description is only used for the multiple choice dialog.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Card.getText()

Postby Chris H. » 12 Sep 2010, 18:38

Yeah, I too have considered similar thoughts. The cards.txt file has gotten so large that it is hard to maintain it without conflicts arising. Imagine a folder with a separate text file for each and every cards. While we are it it we could have a label for each field. This would allow multi-line spell text descriptions without jumping through any additional hoops.

We can always dream.
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: Card.getText()

Postby Rob Cashwalker » 13 Sep 2010, 00:45

I'm quickly warming up to that concept....
Short of figuring out how to iterate all files in a folder, I think I know for sure that by the next beta cards.txt will become a cards.folder.

Edit - I have this working!

This is ReadCard:
Code: Select all
package forge;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;

import forge.error.ErrorViewer;
import forge.properties.ForgeProps;
import forge.properties.NewConstants;


public class ReadCard implements Runnable, NewConstants {
    private BufferedReader  in;
    private String fileList[];
    private ArrayList<Card> allCards = new ArrayList<Card>();
   
    public static void main(String args[]) throws Exception {
        try {
            ReadCard read = new ReadCard(ForgeProps.getFile(CARDS));
           
            javax.swing.SwingUtilities.invokeAndWait(read);
            //    read.run();
           
            Card c[] = new Card[read.allCards.size()];
            read.allCards.toArray(c);
            for(int i = 0; i < c.length; i++) {
                System.out.println(c[i].getName());
                System.out.println(c[i].getManaCost());
                System.out.println(c[i].getType());
                System.out.println(c[i].getSpellText());
                System.out.println(c[i].getKeyword());
                System.out.println(c[i].getBaseAttack() + "/" + c[i].getBaseDefense() + "\n");
            }
        } catch(Exception ex) {
            ErrorViewer.showError(ex);
            System.out.println("Error reading file " + ex);
        }
    }
   
    public ArrayList<Card> getCards() {
        return new ArrayList<Card>(allCards);
    }
   
    public ReadCard(String filename) {
        this(new File(filename));
    }
   
    public ReadCard(File file) {
        if(!file.exists())
            throw new RuntimeException("ReadCard : constructor error -- file not found -- filename is "
                    + file.getAbsolutePath());
       
        if (!file.isDirectory())
           throw new RuntimeException("ReadCard : constructor error -- not a direcotry -- "
                 + file.getAbsolutePath());
       
        fileList = file.list();
        //makes the checked exception, into an unchecked runtime exception
        //try {
        //    in = new BufferedReader(new FileReader(file));
        //} catch(Exception ex) {
        //    ErrorViewer.showError(ex, "File \"%s\" not found", file.getAbsolutePath());
        //    throw new RuntimeException("ReadCard : constructor error -- file not found -- filename is "
        //            + file.getPath());
        //}
    }//ReadCard()
   
    public void run() {
       Card c = null;
       ArrayList<String> cardNames = new ArrayList<String>();
       File fl = null;
       
       for (int i=0; i<fileList.length; i++)
       {
            try {
               fl = new File("res/cardsfolder/" + fileList[i]);
                in = new BufferedReader(new FileReader(fl));
            } catch(Exception ex) {
                ErrorViewer.showError(ex, "File \"%s\" exception", fl.getAbsolutePath());
                throw new RuntimeException("ReadCard : run error -- file exception -- filename is "
                        + fl.getPath());
            }
           
            c = new Card();
           
            String s = readLine();
            while (!s.equals("End"))
            {
               if (s.startsWith("Name:"))
               {
                  String t = s.substring(5);
                  System.out.println(s);
                  if (cardNames.contains(t))
                  {
                        System.out.println("ReadCard:run() error - duplicate card name: " + t);
                        throw new RuntimeException("ReadCard:run() error - duplicate card name: " + t);
                  }
                  else
                     c.setName(t);
               }
               
               else if (s.startsWith("ManaCost:"))
               {
                  String t = s.substring(9);
                  System.out.println(s);
                  if (!t.equals("no cost"))
                     c.setManaCost(t);
               }
               
               else if (s.startsWith("Types:"))
                  addTypes(c, s.substring(6));
               
               else if (s.startsWith("Text:"))
               {
                  String t = s.substring(5);
                  if (!t.equals("no text"));
                     c.setText(t);
               }
               
               else if (s.startsWith("PT:"))
               {
                  String t = s.substring(3);
                  String pt[] = t.split("/");
                  int att = Integer.parseInt(pt[0]);
                  int def = Integer.parseInt(pt[1]);
                  c.setBaseAttack(att);
                  c.setBaseDefense(def);
               }
               
               else if (s.startsWith("K:"))
               {
                  String t = s.substring(2);
                  c.addIntrinsicKeyword(t);
               }
               
               s = readLine();
            } // while !End

            cardNames.add(c.getName());
            allCards.add(c);
           
            try {
            in.close();
         } catch (IOException ex) {
                ErrorViewer.showError(ex, "File \"%s\" exception", fl.getAbsolutePath());
                throw new RuntimeException("ReadCard : run error -- file exception -- filename is "
                        + fl.getPath());
         }
       }
       
    }//run()
   
    private void addTypes(Card c, String types) {
        StringTokenizer tok = new StringTokenizer(types);
        while(tok.hasMoreTokens())
            c.addType(tok.nextToken());
    }
   
    private String readLine() {
        //makes the checked exception, into an unchecked runtime exception
        try {
            String s = in.readLine();
            if(s != null) s = s.trim();
            return s;
        } catch(Exception ex) {
            ErrorViewer.showError(ex);
            throw new RuntimeException("ReadCard : readLine(Card) error");
        }
    }//readLine(Card)
}
This is where CardFactory calls it:
Code: Select all
ReadCard read = new ReadCard(ForgeProps.getFile(CARDSFOLDER));
These are the first two cards converted:
abandoned_outpost.txt: (filenames really don't matter)
Code: Select all
Name:Abandoned Outpost
ManaCost:no cost
Types:Land
Text:no text
K:tap: add W
K:tap, Sacrifice CARDNAME: Add W to your mana pool.
K:tap, Sacrifice CARDNAME: Add U to your mana pool.
K:tap, Sacrifice CARDNAME: Add B to your mana pool.
K:tap, Sacrifice CARDNAME: Add R to your mana pool.
K:tap, Sacrifice CARDNAME: Add G to your mana pool.
K:Comes into play tapped.
K:SVar:Rarity:Common
K:SVar:Picture:http://www.wizards.com/global/images/magic/general/abandoned_outpost.jpg
End
abbey_gargoyles.txt:
Code: Select all
Name:Abbey Gargoyles
ManaCost:2 W W W
Types:Creature Gargoyle
Text:no text
PT:3/4
K:Flying
K:Protection from red
K:SVar:Rarity:Uncommon
K:SVar:Picture:http://www.wizards.com/global/images/magic/general/abbey_gargoyles.jpg
End
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Card.getText()

Postby Chris H. » 13 Sep 2010, 13:05

Wow. I am glad that I came back and took another look at your message. :D The additional work that you appended onto the end of your message is awesome!

As far as file names I think you have the right idea. That system works well for the jpg pics and I can't think of any reasons why this should not be used.

Would Sol be able to write a python script to create the card files from the existing cards.txt file?

I guess that this would be a several step sort of process. Divide up the cards.txt file and add in your code. Bug test and fix any problems that come up.

Once things are stable we can then consider what it would take to replace all of the values included in the "Text:" field with the rules data at gathers. Comment out the code inside of Card.getText().

We may have some card and/or keyword code that would then have to be modified to handle the new system. The gather rules data would also need a little editing to add in the missing notes and we have the braces to remove.
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: Card.getText()

Postby Rob Cashwalker » 13 Sep 2010, 13:54

I'm thinking I can actually re-hack the original ReadCard methods to make it spit out what we need...

Once it's broken out, it shouldn't be a huge change for the python script to maintain.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Card.getText()

Postby Rob Cashwalker » 13 Sep 2010, 20:54

This is the output from hacking ReadCard to also dump it to individual card files.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Card.getText()

Postby Chris H. » 13 Sep 2010, 22:54

I downloaded the archive. Nice work. When should we make the change over?
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: Card.getText()

Postby Rob Cashwalker » 14 Sep 2010, 02:54

Well, I uploaded everything.

You changed Arashi, the Sky Asunder between the afternoon and evening, that will need to be re-applied.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Card.getText()

Postby friarsol » 14 Sep 2010, 03:04

Rob Cashwalker wrote:Well, I uploaded everything.

You changed Arashi, the Sky Asunder between the afternoon and evening, that will need to be re-applied.
We should probably put a post up on the change so everyone is aware of the new format. And probably actually submit the new folder to the SVN. This will make mass-changes more difficult since devs can no longer just search through one file for a specific string.
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: Card.getText()

Postby DennisBergkamp » 14 Sep 2010, 03:41

Yup, I don't see any of the individual card files committed....
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Card.getText()

Postby Rob Cashwalker » 14 Sep 2010, 03:46

I selected all the card files and then did "Add to Version control". In fact, I did that before I committed the source code changes.

I just did a commit, see if that makes them show up.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Card.getText()

Postby DennisBergkamp » 14 Sep 2010, 05:33

Now I do, great stuff :)
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Card.getText()

Postby Rob Cashwalker » 14 Sep 2010, 11:36

FYI - Google's SVN chokes on this many files.... And it seems like it doubles the data transmitted.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Card.getText()

Postby Chris H. » 14 Sep 2010, 12:43

Rob Cashwalker wrote:Well, I uploaded everything.

You changed Arashi, the Sky Asunder between the afternoon and evening, that will need to be re-applied.
`
No problem, it was a simple replace "T:" with "tap:" change for readability. I can make this mod once the new card files are all in place. :D
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Next

Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 57 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 57 users online :: 0 registered, 0 hidden and 57 guests (based on users active over the past 10 minutes)
Most users ever online was 7303 on 15 Jul 2025, 20:46

Users browsing this forum: No registered users and 57 guests

Login Form