It is currently 30 Apr 2025, 18:51
   
Text Size

Building Forge

Post MTG Forge Related Programming Questions Here

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

Building Forge

Postby Hellfish » 30 May 2010, 12:35

Hey!

I was looking at fiddling with the forge source a bit, and could use a few pointers from the pros ;)
I've got the package opened in eclipse (after alot of nerve grating. Coming from Visual Studio it's just so unintuitive.. Eh, it'll probably pass) and I can change things and run the changed version just fine (AFAICT anyway, I've only successfully changed the Run.java starting cards. I've got a few problems though.

Firstly, I can add cards to my play area (M2010 terminology be damned :? ) just fine, but when I try to add cards to my hand, they don't show up.They're just not there. The card counter to the left shows them, but the're not visible and can't be used.

Secondly, I tried adding a card just to try it out. Using Totem-Guide Hartebeest as a base I just made a simple creature, added an elseif case at the end of CardFactory_Creatures, and added it in cards.txt. But when I try to place it in the play area, cardfactory throws an "invalid card name" exception. There are more steps to adding a card that I'm missing, isn't there? :oops:

And lastly, this is just a little niggle I can work around if there's nothing to be done about it, but when I finish testing and want to close the test forge I always have to use the task manager to close down the javaw process or get stuck at the "You Lose" window.Any way around that?

Thanks for reading, and thanks in advance for any help at all. Any help understanding this fascinating monolith is appreciated. :)
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: Building Forge

Postby DennisBergkamp » 30 May 2010, 19:45

Hmm, maybe I'm missing something here completely... but after compiling forge, which file are you using as the main class? It should be Gui_NewGame.

The invalid cardname error must mean there's some incorrect formatting in cards.txt. Can you post the entry?
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Building Forge

Postby Hellfish » 30 May 2010, 20:11

I was using Run.java for the ability to specify cards in play. Guess I shouldn't. ;)
And yeah, turns out there was an error in cards.txt... I was modifying the wrong one. :oops:
I had another workspace with forge after subclipse confused me earlier...
Using Gui_Newgame.java instead also (of course ;)) fixes the invisible hand problem so woop woop, I'm good to go!

Much obliged! :)
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: Building Forge

Postby Beached As » 30 May 2010, 22:02

For a guide to make any card without doing too much new programming, just find a card in cards.txt which is similar to the card you want to implement (It's good to search for a card which has the same timing i.e. during your upkeep or until end of turn, and the same effect i.e. destroying a creature or putting a +1 +1 counter on something). Then open windows explorer and search the card name in the workspace folder for eclipse. This will come up with files which have code which makes the searched card work. Then just copy and paste to make a new card and edit the pasted code to fit the new cards description.
Beached As
Programmer
 
Posts: 110
Joined: 23 Feb 2010, 07:48
Has thanked: 0 time
Been thanked: 0 time

Re: Building Forge

Postby Chris H. » 31 May 2010, 00:10

Beached As wrote:For a guide to make any card without doing too much new programming, just find a card in cards.txt which is similar to the card you want to implement (It's good to search for a card which has the same timing i.e. during your upkeep or until end of turn, and the same effect i.e. destroying a creature or putting a +1 +1 counter on something). Then open windows explorer and search the card name in the workspace folder for eclipse. This will come up with files which have code which makes the searched card work. Then just copy and paste to make a new card and edit the pasted code to fit the new cards description.
`
This is a good way to get started. I am using a similar type of searching system on my Mac to update the keywords to use the new CARDNAME keyword.

I have also used a copy/paste/edit system for some individual cards and have also used this to implement several keywords that are based on existing code. :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

Re: Building Forge

Postby Hellfish » 31 May 2010, 11:08

Yeah, that's basically what I'm doing. :)
As a starting project I'm tinkering with something that will allow for instance Cursed Rack and Thought Devourer to be easily implemented. With any luck it will pass mustard in testing tonight ;)
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: Building Forge

Postby Hellfish » 31 May 2010, 20:22

Update: I didn't get as much time to test things out as I'd have liked but what I've done is add a keyword "HandSize". It can set,increase or decrease target player's(Self,Opponent or All) maximum hand size or remove the maximum altogether.Theoretically and seemingly, card removal,controller changes and interaction between multiple HandSize cards should work (Wish I had time to test it tonight :P) with the system I've set up.

If anybody would like to take a look at it, tell me how you'd like the code served :) It might need some streamlining for ,to an actual Java programmer, obvious optimizations and to follow Forge coding conventions.

The cards that are no problem with this keyword:
Cursed Rack
Gnat Miser
Graceful Adept
Locust Miser
Minamo Scrollkeeper
Reliquary Tower
Spellbook
Thought Devourer
Thought Eater
Thought Nibbler

Cards that need another effect or ability I havn't worked out yet:
Anvil of Bogardan
Library of Leng
Null Profusion
Recycle
Trusted Advisor

The format for cards.txt is
HandSize 1 2 3
(Space separates parameters, this might violate convention, I realise now.)
1 = Mode. It can be "="(Set),"+"(Increase),"-"(Decrease) No quotes.
2 = Amount. How much to increase or decrease, or what to set the maximum hand size of the target to.
3 = Target. It can be "Self"(the card's controller),"Opponent"(Controller's opponent) or "All".

Amount has a special constant called INF(inite) for the cards that state "You have no maximum hand size.". INF should really only be used with Set mode, but can technically be used with any of them since it get's replaced with -1.

So for example:
Code: Select all
Graceful Adept
2 U
Creature Human Wizard
You have no maximum hand size.
1/3
HandSize = INF Self

Thought Devourer
2 U U
Creature Beast
Your maximum hand size is reduced by four.
4/4
HandSize - 4 Self
Flying
Thanks for the tips and pointers, couldn't have done without em! :D
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: Building Forge

Postby DennisBergkamp » 31 May 2010, 22:44

Hellfish,

Cool stuff! Well, there's two options, either just post the code here, OR commit it to the SVN yourself (I'll give you committer privileges). Either way should work fine :mrgreen:
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Building Forge

Postby Hellfish » 01 Jun 2010, 07:07

Patch is here. The way it works is that Input_Cleanup and Computer_Cleanup holds a variable MaximumHandSize and a list of operations to perform on it. After continuous effects (as per rule 613.10) both MaximumHandSize vars are recalculated by looping through their respective list and applying the operations in effectively timestamp order. Every card with the HandSize keyword gets three commands registered: entersPlay,leavesPlay and changesController that all just add and remove the maximum hand size operations as appropriate.

I'd feel better if it was looked over before committing, I'm sure you would too ;)

DESTRUCTIVE EDIT: Check further down for improved code.
Last edited by Hellfish on 02 Jun 2010, 08:29, edited 1 time in total.
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: Building Forge

Postby DennisBergkamp » 01 Jun 2010, 16:50

Thanks Hellfish, I will merge your code asap :mrgreen:
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Building Forge

Postby Hellfish » 02 Jun 2010, 07:20

Thank you, Dennis, Much appreciated. One more thing though that I missed (I know I know, they're piling up, but hey it's my first keyword ;)) is that the snippet for CardFactory and CardFactory_Creatures must be added to CardFactory_Lands too, for Reliquary Tower to work.

EDIT: Sorry for the annoying amount of trouble, Dennis,(maybe your committer privileges idea was better after all :|) but I found a problem with how the code handles controller changes which could cause the handSizeOperations to be performed in the wrong order, potentially giving a player the wrong hand size. To fix it I will basically need a sort of timestamp for all cards but while I could easily do this for just cards with HandSize, isn't this something that every card should have? (Not to mention that that opens up a whole other can of worms) Fixed code here either way:

In Input_Cleanup.java,add:
Code: Select all
private static int NextHandSizeStamp = 0;
   
    public static int GetHandSizeStamp() {
       return NextHandSizeStamp++;
    }
In Input_Cleanup.java and Computer_Cleanup.java:
Code: Select all
private static List<HandSizeOp> handSizeOperations = new LinkedList<HandSizeOp>();
    private static int MaxHandSize = 7;

public static void sortHandSizeOperations() {
       if(handSizeOperations.size() < 2) {
          return;
       }
       
       Object arr[] = handSizeOperations.toArray();
       int changes = 1;
 
       while(changes > 0) {
          changes = 0;
          for(int i=1;i<arr.length;i++) {
              if(((HandSizeOp)arr[i]).hsTimeStamp < ((HandSizeOp)arr[i-1]).hsTimeStamp) {
                 HandSizeOp tmp = (HandSizeOp)arr[i];
                 arr[i] = arr[i-1];
                 arr[i-1] = tmp;
                 changes++;
              }
           }
       }
        handSizeOperations.clear();
        for(int i=0;i<arr.length;i++) {
                handSizeOperations.add((HandSizeOp)arr[i]);
        }
    }

    public static void calcMaxHandSize() {
       
       int ret = 7;
       for(int i=0;i<handSizeOperations.size();i++)
       {
          if(handSizeOperations.get(i).Mode.equals("="))
          {
             ret = handSizeOperations.get(i).Amount;
          }
          else if(handSizeOperations.get(i).Mode.equals("+") && ret >= 0)
          {
             ret = ret + handSizeOperations.get(i).Amount;
          }
          else if(handSizeOperations.get(i).Mode.equals("-") && ret >= 0)
          {
             ret = ret - handSizeOperations.get(i).Amount;
             if(ret < 0) {
                ret = 0;
             }
          }
       }
       MaxHandSize = ret;
    }
    public static void addHandSizeOperation(HandSizeOp theNew)
    {
       handSizeOperations.add(theNew);
    }
    public static void removeHandSizeOperation(int timestamp)
    {
       for(int i=0;i<handSizeOperations.size();i++)
       {
          if(handSizeOperations.get(i).hsTimeStamp == timestamp)
          {
             handSizeOperations.remove(i);
             break;
          }
       }
    }
In CardFactory.java, CardFactory_Lands.java and CardFactory_Creatures:
Code: Select all
if(hasKeyword(card,"HandSize") != -1) {
            String toParse = card.getKeyword().get(hasKeyword(card,"HandSize"));
            card.removeIntrinsicKeyword(toParse);
           
            String parts[] = toParse.split(" ");
            final String Mode = parts[1];
            final int Amount;
            if(parts[2].equals("INF")) {
               Amount = -1;
            }
            else {
               Amount = Integer.parseInt(parts[2]);
            }
            final String Target = parts[3];
           
            final Command entersPlay,leavesPlay, controllerChanges;
           
            entersPlay = new Command() {
               private static final long serialVersionUID = 98743547743456L;
               
               public void execute() {
                 card.setSVar("HSStamp","" + Input_Cleanup.GetHandSizeStamp());
                  if(card.getController() == Constant.Player.Human) {
                     //System.out.println("Human played me! Mode(" + Mode + ") Amount(" + Amount + ") Target(" + Target + ")" );
                     if(Target.equals("Self")) {
                        Input_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                     }
                     else if(Target.equals("Opponent")) {
                        Computer_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                     }
                     else if(Target.equals("All")) {
                        Computer_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                        Input_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                     }
                  }
                  else
                  {
                     //System.out.println("Compy played me! Mode(" + Mode + ") Amount(" + Amount + ") Target(" + Target + ")" );
                     if(Target.equals("Self")) {
                        Computer_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                     }
                     else if(Target.equals("Opponent")) {
                        Input_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                     }
                     else if(Target.equals("All")) {
                        Computer_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                        Input_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                     }
                  }
               }
            };
           
            leavesPlay = new Command() {
               private static final long serialVersionUID = -6843545358873L;
               
               public void execute() {
                  if(card.getController() == Constant.Player.Human) {
                     if(Target.equals("Self")) {
                        Input_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                     }
                     else if(Target.equals("Opponent")) {
                        Computer_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                     }
                     else if(Target.equals("All")) {
                        Computer_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                        Input_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                     }
                  }
                  else
                  {
                     if(Target.equals("Self")) {
                        Computer_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                     }
                     else if(Target.equals("Opponent")) {
                        Input_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                     }
                     else if(Target.equals("All")) {
                        Computer_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                        Input_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                     }
                  }
               }
            };
           
            controllerChanges = new Command() {
               private static final long serialVersionUID = 778987998465463L;
               
               public void execute() {
                  System.out.println("Control changed: " + card.getController());
                  if(card.getController().equals(Constant.Player.Human)) {
                     Input_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                     Computer_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                     
                     Computer_Cleanup.sortHandSizeOperations();
                  }
                  else if(card.getController().equals(Constant.Player.Computer)) {
                     Computer_Cleanup.removeHandSizeOperation(Integer.parseInt(card.getSVar("HSStamp")));
                     Input_Cleanup.addHandSizeOperation(new HandSizeOp(Mode,Amount,Integer.parseInt(card.getSVar("HSStamp"))));
                     
                     Input_Cleanup.sortHandSizeOperations();
                  }
               }
            };
           
            card.addComesIntoPlayCommand(entersPlay);
            card.addLeavesPlayCommand(leavesPlay);
            card.addChangeControllerCommand(controllerChanges);
         } //HandSize
Create HandSizeOp.java:
Code: Select all
package forge;

public class HandSizeOp {
   public String Mode;
   public int hsTimeStamp;
   public int Amount;
   
   public HandSizeOp(String M,int A,int TS)
   {
      Mode = M;
      Amount = A;
      hsTimeStamp = TS;
   }
   
   public String toString()
   {
      return "Mode(" + Mode + ") Amount(" + Amount + ") Timestamp(" + hsTimeStamp + ")";
   }
}
In Phase.nextPhase(), after GameActionUtil.executeCardStateEffects():
Code: Select all
        Input_Cleanup.calcMaxHandSize();
        Computer_Cleanup.calcMaxHandSize();
cards.txt entries:
Code: Select all
Cursed Rack
4
Artifact
As Cursed Rack enters the battlefield, choose an opponent.The chosen player's maximum hand size is four.
HandSize = 4 Opponent

Gnat Miser
B
Creature Rat Shaman
Each opponent's hand size is reduced by one.
1/1
HandSize - 1 Opponent

Graceful Adept
2 U
Creature Human Wizard
You have no maximum hand size.
1/3
HandSize = INF Self

Locust Miser
2 B B
Creature Rat Shaman
Each opponent's maximum hand size is reduced by two.
2/2
HandSize - 2 Opponent

Minamo Scrollkeeper
1 U
Creature Human Wizard
Your maximum hand size is increased by one.
2/3
HandSize + 1 Self
Defender

Reliquary Tower
no cost
Land
You have no maximum hand size.
HandSize = INF Self
tap: add 1

Spellbook
0
Artifact
You have no maximum hand size.
HandSize = INF Self

Thought Devourer
2 U U
Creature Beast
Your maximum hand size is reduced by four.
4/4
HandSize - 4 Self
Flying

Thought Eater
1 U
Creature Beast
Your maximum hand size is reduced by three.
2/2
HandSize - 3 Self
Flying

Thought Nibbler
U
Creature Beast
Your maximum hand size is reduced by two.
1/1
HandSize - 2 Self
Flying
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: Building Forge

Postby DennisBergkamp » 07 Jun 2010, 20:24

Ugh, I forgot to merge this for the 0605 version :oops: ... I'll try to add it into the SVN soon.
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Building Forge

Postby Hellfish » 08 Jun 2010, 03:59

No worries, it's not like this is a paid job :) Besides, I've been too busy actually *playing* 0605 to notice :lol:
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: Building Forge

Postby DennisBergkamp » 15 Jun 2010, 16:39

I've just merged your code, Hellfish... no compile errors, but there must still be something missing: after tests with Spellbook and Cursed Rack, they don't seem to do anything. Anyway, I'll upload this to SVN, maybe you could see what's going on.
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Building Forge

Postby Hellfish » 15 Jun 2010, 18:04

#-o

I forgot the actual enforcement of the altered hand size in the fixed version.. Here it is:
In Input_Cleanup.showMessage(), change from:
Code: Select all
if(n <= 7) {
to:
Code: Select all
if(n <= MaxHandSize || MaxHandSize == -1) {
In Computer_Cleanup.showMessage(), change from:
Code: Select all
while(7 < c.length) {
to:
Code: Select all
while(MaxHandSize < c.length && MaxHandSize != -1) {
Sorry! :oops:
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Next

Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 32 guests


Who is online

In total there are 32 users online :: 0 registered, 0 hidden and 32 guests (based on users active over the past 10 minutes)
Most users ever online was 4143 on 23 Jan 2024, 08:21

Users browsing this forum: No registered users and 32 guests

Login Form