It is currently 26 Apr 2024, 10:28
   
Text Size

CardFactory.java

Post MTG Forge Related Programming Questions Here

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

CardFactory.java

Postby Chris H. » 12 Sep 2009, 15:52

The following quote is fairly important and I decided to split it off into it's own forum topic.

In message:

Re: spPumpTgt - Targeted Pump Spells

Rob Cashwalker wrote:The trick with multiple keywords, like spPumpTgt and Cycling is that the order of processing in CardFactory.java needs to be changed. The permanent ability keywords need to be first. Then the spell keywords need to be second. Then the "tack-on" keywords need to be processed last, possibly at the very end of CardFactory, allowing for "tack-ons" to be used for explicitly coded cards.

Cantrip isn't implemented like the others. It's checked upon resolution of any spell - if the card contains the keyword, then it just draws a card. Quite separately from any other card code.

The tricky part I've been trying to think about lately, is the Charms or Commands. Each individual ability of Chaos Charm for example, is something that we have keywords for. But we can't just do it like this:

Chaos Charm
R
Instant
Choose one - Destroy target Wall; or Chaos Charm deals 1 damage to target creature; or target creature gains haste until end of turn.
spDestroyTgt:Creature.Wall
spDamageC:1 (OK, we don't have creature-only damage yet, but it's easy enough)
spPumpTgt:Haste

Because each of the sp___ code blocks clears out the spell abilities of the card. (because all cards are assumed to be permanents, and given a default spell ability to be put into play).
So it would process spDestroyTgt (never mind the fact that the code needs the text formatted a certain way in order to extract the input dialog text) removing the default ability.
Then it would process the spDamageC ability, removing the spDestroyTgt abilty.
Then it would process the spPumpTgt ability, removing the spDamageC ability.
And that's just a rough example, because that's not the order of code blocks... spDamageC would be first, up by the spDamageCP and spDamageP blocks; then spDestroyTgt, and spPumpTgt would probably be inserted last when Dennis merges it.

I think the best way to handle this may be something like the following:
Code: Select all
public boolean hasSpKeyword()
{
  ArrayList k = card.getKeywords();
  for (i = 0; i < k.length; i++)
    if (k(i).startsWith("sp")
      return true;
  return false;
}
Code: Select all
if (hasSpKeyword)
{
   card.clearSpellAbility();

   if (shouldSpDamageCP)
   {
      ...
   }
   if (shouldSpDamageP)
   {
      ...
   }
   if (shouldSpDestroyTgt)
   {
      ...
   }
   if (shouldSpPumpTgt)
   {
      ...
   }
   ...
      etc
   ...
}
Then remove the clearSpellAbility() from each individual code block. Oh, and make sure each one has a spell.setDescription().

When you go to play the spell, the game will now present each spell option. This won't work for the Commands, which want two choices... but that's OK... for now.
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: CardFactory.java

Postby Chris H. » 12 Sep 2009, 15:58

I spent a little time looking at the new cards for Planechase. I found the spell Akroma's Vengeance and it is very close to the spell Jokulhaups. Initially, I was planning to do a copy + paste and then edit the second copy. I am still having problems figuring out how to set a new "private static final long serialVersionUID".

So I decided to edit the code for Jokulhaups so that it would cover both spells. Looking at the code I discovered that "||" represents OR in if statements. I guessed that "&" would represent AND.

The code changes worked fine in the test deck that I created. Except for the fact that Akroma's Vengeance has Cycling:3 :(


Akroma's Vengeance
4 W W
Sorcery
Destroy all artifacts, creatures, and enchantments.
Cycling:3


Code: Select all
    //*************** START *********** START **************************
    if(cardName.equals("Jokulhaups") || cardName.equals("Akroma's Vengeance"))
    {
      final SpellAbility spell = new Spell(card)
      {
      private static final long serialVersionUID = -7384618531690849205L;

      public void resolve()
        {
          CardList all = new CardList();
          all.addAll(AllZone.Human_Play.getCards());
          all.addAll(AllZone.Computer_Play.getCards());

          for(int i = 0; i < all.size(); i++)
          {
            Card c = all.get(i);
           
            if(cardName.equals("Jokulhaups") & (c.isCreature() || c.isArtifact() || c.isLand()))
                AllZone.GameAction.destroyNoRegeneration(c);
             
            if(cardName.equals("Akroma's Vengeance") & (c.isCreature() || c.isArtifact() || c.isEnchantment()))
                  AllZone.GameAction.destroy(c);
          }
        }//resolve()
      };//SpellAbility
      card.clearSpellAbility();
      card.addSpellAbility(spell);
    }//*************** END ************ END **************************
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: CardFactory.java

Postby DennisBergkamp » 12 Sep 2009, 17:31

Ahh, awesome. I haven't started creating cards yet for the new set, but I think I'll try adding a few this weekend :)

By the way, the Cycling part probably won't work. I've had the same problem with Hush I think. So I had to add in the Cycling code separately, because a card.clearSpellAbility() gets called...

This should do the trick (check at the end) :

Code: Select all
    //*************** START *********** START **************************
    if(cardName.equals("Jokulhaups") || cardName.equals("Akroma's Vengeance"))
    {
      final SpellAbility spell = new Spell(card)
      {
      private static final long serialVersionUID = -7384618531690849205L;

      public void resolve()
        {
          CardList all = new CardList();
          all.addAll(AllZone.Human_Play.getCards());
          all.addAll(AllZone.Computer_Play.getCards());

          for(int i = 0; i < all.size(); i++)
          {
            Card c = all.get(i);
           
            if(cardName.equals("Jokulhaups") & (c.isCreature() || c.isArtifact() || c.isLand()))
                AllZone.GameAction.destroyNoRegeneration(c);
             
            if(cardName.equals("Akroma's Vengeance") & (c.isCreature() || c.isArtifact() || c.isEnchantment()))
                  AllZone.GameAction.destroy(c);
          }
        }//resolve()
      };//SpellAbility
      card.clearSpellAbility();
      card.addSpellAbility(spell);
      if (cardName.equals("Akroma's Vengeance")) //add cycling
               card.addSpellAbility(CardFactoryUtil.ability_cycle(card, "3"));
    }//*************** END ************ END **************************
By the way, for adding the serialVersionUID thing:
Eclipse will start complaining and give a warning whenever there's a serializable class that doesn't have a serialVersionUID. You'll see this through a yellow underline under the class declaration and a small yellow rectangle on the right hand side. Just hover over the yellow underline and select "Add generated serial version ID".
Attachments
serialversionID.jpg
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: CardFactory.java

Postby Chris H. » 12 Sep 2009, 18:24

Wow, thank you. :D

I saw the "card.clearSpellAbility();" at the bottom and felt that it was probably removing the cycling keyword. I felt that there was a way to add it back in, just wasn't sure how to handle this situation.

I guess that I could search the list for cycle cards and I might find a few that can be added in this way. Some might require a change to cards.txt and I think that I can handle several keyword cards + cycle in a single section of code. 8)

I was able to get the small yellow window to show up but I was unsure which of the choices should be made.
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: CardFactory.java

Postby mtgrares » 12 Sep 2009, 20:02

At the beginning of CardFactory for every card I called card.addSpellAbility(new Spell_Permanent()) to give the creatures in cards.txt a "default" spells, which summons a permanent such as an artifact, enchantment, land, planeswalker etc... So all instants and sorceries have to remove the permanent SpellAbility by calling card.clearSpellAbility().

Hopefully that explains things a little bit.
mtgrares
DEVELOPER
 
Posts: 1352
Joined: 08 Sep 2008, 22:10
Has thanked: 3 times
Been thanked: 12 times

Re: CardFactory.java

Postby Chris H. » 13 Sep 2009, 10:47

mtgrares wrote:At the beginning of CardFactory for every card I called card.addSpellAbility(new Spell_Permanent()) to give the creatures in cards.txt a "default" spells, which summons a permanent such as an artifact, enchantment, land, planeswalker etc... So all instants and sorceries have to remove the permanent SpellAbility by calling card.clearSpellAbility().

Hopefully that explains things a little bit.
Thanks Rares.

That explains how in one of my tests a spell with Flashback ended up in play on the battlefield and in the graveyard/Flashback at the same time.



I would also like to comment on how editing the CardFactory.java file in Eclipse will cause my Macintosh to pause for a long time with a spinning beachball. And my computer is only a few months old and has a 3.06 GHz Intel Core 2 Duo. :shock:

The CardFactory.java file must be so large that Eclipse is having a hard time moving data around in memory.

At one time Rob and Dennis were considering splitting up the CardFactory.java file into smaller pieces. They ran into a few problems.

I wonder if Rares, Zeerker or Silly Freak have any ideas on how this could be handled. Splitting up the CardFactory.java file into smaller pieces could also include Rob's ideas that are quoted in the first message on this thread.
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: CardFactory.java

Postby Rob Cashwalker » 13 Sep 2009, 14:16

DennisBergkamp wrote:By the way, the Cycling part probably won't work. I've had the same problem with Hush I think. So I had to add in the Cycling code separately, because a card.clearSpellAbility() gets called...
This is what I was trying to point out in the quote:

The code that checks for cycling as a keyword needs to be moved to the end of either the keyword code block, or the end of CardFactory itself. Then it is added on AFTER any clearSpellAbility().

Same thing for Flashback and Unearth, I would guess.

The other point I made was that the keyword code block could start off with a check for the "sp" prefix added to spell keywords. If present, it clears the spellability BEFORE actually defining the execution code. (and the clearSpellAbility is removed from the code definition)
This would open up a simple way of choosing single mode charm spells.
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: CardFactory.java

Postby zerker2000 » 13 Sep 2009, 21:43

And you cannot just have "default" code check for the card being a creature/land/artifact/enchantment/planeswalker/plane why?
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: CardFactory.java

Postby mtgrares » 14 Sep 2009, 14:34

zerker2000 wrote:And you cannot just have "default" code check for the card being a creature/land/artifact/enchantment/planeswalker/plane why?
That would work better. Different things makes sense at different times. I usually coded first and thought second. While this isn't a really great way to do things, at least I never got stuck anywhere. (And that also explains CardFactory, the world's longest method.)

p.s.
I did think long and hard about the Input class (which handles ALL of the mouse input) but it is very confusing to explain.
mtgrares
DEVELOPER
 
Posts: 1352
Joined: 08 Sep 2008, 22:10
Has thanked: 3 times
Been thanked: 12 times

Re: CardFactory.java

Postby zerker2000 » 15 Sep 2009, 02:09

mtgrares wrote:
zerker2000 wrote:And you cannot just have "default" code check for the card being a creature/land/artifact/enchantment/planeswalker/plane why?
That would work better. Different things makes sense at different times. I usually coded first and thought second. While this isn't a really great way to do things, at least I never got stuck anywhere. (And that also explains CardFactory, the world's longest method.)
My point is that it should be a quick fix: edit the "default code" to include
Code: Select all
#buyback code#
boolean tofield=false;
boolean tograve=false;
String permanents = "Creature/Land/Artifact/Enchantment/Planeswalker/Plane"
for(String type: card.getTypes().split(" ");)
{
  if (permanents.contains(type)) tofield=true;
  if (type.equals("Instant")|| type.equals("Sorcery")) tograve=true;
}
if(tofield!^tograve) throw (#something heavy at developers#);
if(tofield) {#old code#}
if(tograve) {#old Instant / Sorcery casting code#}
, remove now unnecessary clearSpellAbilit-ies, spend about ten times that time debugging :P, and you're done.
p.s.
I did think long and hard about the Input class (which handles ALL of the mouse input) but it is very confusing to explain.
It seems OK to me... the whole targeting system could be tweaked to allow easier multi-target spells, but that is easy enough to do on a case-by-case basis (i.e. new Input[] example; example[0]= new Input{final target aquisition};<complicated but writeable Iterator code that defines example[next]'s recursively>) and otherwise, I haven't run into any other problems with Input.
EDIT: Btw something in eclipse died and I haven't had the time to reanimate it, so be sure to check all code I'll be posting in the near future for forge method name and trivial grammar errors.
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: CardFactory.java

Postby mtgrares » 15 Sep 2009, 18:07

It seems OK to me... the whole targeting system could be tweaked to allow easier multi-target spells.
Yeah, the current system presume that a card will only have one target, which works for many cards but definitely not all. This was an mistake that I have fixed in the 2.0 Input class. It has methods like addTargetCard(Card), countTargetCard() : int, and getTargetCard(int index) so many cards could be targeted easily instead of the "hacking" that is needed for cards like Hex, which can be done but they are messy and the computer can't play it.
mtgrares
DEVELOPER
 
Posts: 1352
Joined: 08 Sep 2008, 22:10
Has thanked: 3 times
Been thanked: 12 times


Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 99 guests


Who is online

In total there are 99 users online :: 0 registered, 0 hidden and 99 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 99 guests

Login Form