Programming a card
Post MTG Forge Related Programming Questions Here
Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins
Re: Programming a card
by silly freak » 18 Oct 2009, 21:12
actually, the point of the forge.properties file is that you don't have to copy your res folder. you can change the forge.properties file to point wherever you want. my folder structure looks like
(okay, that's not exactly what it looks for me; i just share the pics folder, just to be save from incompatible res files. but that's only my paranoia...)
- Code: Select all
forge
forge/versions
forge/versions/20091007
forge/versions/20091015
forge/versions/20091017
forge/res
- Code: Select all
main--transparent-properties=../../res/main.properties
(okay, that's not exactly what it looks for me; i just share the pics folder, just to be save from incompatible res files. but that's only my paranoia...)
___
where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
- silly freak
- DEVELOPER
- Posts: 598
- Joined: 26 Mar 2009, 07:18
- Location: Vienna, Austria
- Has thanked: 93 times
- Been thanked: 25 times
Re: Programming a card
by cyclope » 19 Oct 2009, 19:08
Thanks for your help with using Eclipse...
But as Zerker said , can i use the abdamage keyword or no for Nightscape Master ?
But as Zerker said , can i use the abdamage keyword or no for Nightscape Master ?
- cyclope
- Posts: 69
- Joined: 28 Sep 2009, 18:08
- Has thanked: 0 time
- Been thanked: 0 time
Re: Programming a card
by DennisBergkamp » 19 Oct 2009, 20:12
There's currently an abDamageCP keyword, but it also targets players, so I'm not sure if that would work for Nightscape Master.
A possibility would be to create an abDamageP keyword
A possibility would be to create an abDamageP keyword

-
DennisBergkamp - AI Programmer
- Posts: 2602
- Joined: 09 Sep 2008, 15:46
- Has thanked: 0 time
- Been thanked: 0 time
Re: Programming a card
by Chris H. » 19 Oct 2009, 20:26
`cyclope wrote:can i use the abdamage keyword or no for Nightscape Master ?
Permanents which use the abDamageCP keyword will ask you to "Select target Creature, Player, or Planeswalker" as part of it's activation and targeting system. You can look at the abDamageCP keyword code. You may be able to copy and modify a portion of the code to use with the Nightscape Master code.
Unfortunately, the keyword by itself will not give you quite the ability that you are looking for ... it is close.
-
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: Programming a card
by Rob Cashwalker » 20 Oct 2009, 18:49
abDamageC, abDamageP and spDamageP represented too few cards that I knew how to do back then, many of them rely on variable information, which I didn't figure out how to program until spDraw and spDamageTgt.
spDamageTgt represents a way to code a single effect that may target either Creatures only, Players only or Creatures or Players (I know it asks the human for planeswalker targets, but I forgot about the AI). Previous keywords were separate because they had different AI due to the difference in targets or other details.
Basically, look for the sequence "CP" in the keyword string (k[0]) ELSE (important) look for "C" ELSE look for "P".
Set a final boolean to represent each specific case, and set up the AI to choose accordingly.
Players should be higher priority than creatures if their life would drop below a threshold of like 10 or so after the damage.
Walkers probably need to drop below 2 or 3, or half their printed loyalty, or (something else?).
Notice that the AI is broken up into smaller decision functions, each of which is used depending on the variant of damage spell.
Having overcome the slight AI differences, I'd like to go back over the keywords to make them work in a uniform fashion, which could eliminate a few thousand lines of code in just the keyword handling section, not to mention any explicit code for similar cards that needed it. Adding support for the variables and drawbacks also opens up all the cards that were ignored previously because of their difficulty in implementation.
I don't think we should go as far as unifying abilities and spells, because spells have their SpellAbilities cleared and permanents don't, but that might not be difficult to revise later as well.
Just consolidating the various forms of Pump into "abPump[Tgt] {cost}:{Boosts}" would be huge. ([Tgt] is optional if present, it's targeted, if not, it's a self-pump. If there is only one element in the Boosts parameter, then it's a keyword-only pump, if there are two elements, it's a PT-only pump, if there are three elements it's a PTK pump. Simple enough? This is how the spPumpTgt works, but without the support for variables)
spDamageTgt represents a way to code a single effect that may target either Creatures only, Players only or Creatures or Players (I know it asks the human for planeswalker targets, but I forgot about the AI). Previous keywords were separate because they had different AI due to the difference in targets or other details.
Basically, look for the sequence "CP" in the keyword string (k[0]) ELSE (important) look for "C" ELSE look for "P".
Set a final boolean to represent each specific case, and set up the AI to choose accordingly.
Players should be higher priority than creatures if their life would drop below a threshold of like 10 or so after the damage.
Walkers probably need to drop below 2 or 3, or half their printed loyalty, or (something else?).
Notice that the AI is broken up into smaller decision functions, each of which is used depending on the variant of damage spell.
Having overcome the slight AI differences, I'd like to go back over the keywords to make them work in a uniform fashion, which could eliminate a few thousand lines of code in just the keyword handling section, not to mention any explicit code for similar cards that needed it. Adding support for the variables and drawbacks also opens up all the cards that were ignored previously because of their difficulty in implementation.
I don't think we should go as far as unifying abilities and spells, because spells have their SpellAbilities cleared and permanents don't, but that might not be difficult to revise later as well.
Just consolidating the various forms of Pump into "abPump[Tgt] {cost}:{Boosts}" would be huge. ([Tgt] is optional if present, it's targeted, if not, it's a self-pump. If there is only one element in the Boosts parameter, then it's a keyword-only pump, if there are two elements, it's a PT-only pump, if there are three elements it's a PTK pump. Simple enough? This is how the spPumpTgt works, but without the support for variables)
The Force will be with you, Always.
-
Rob Cashwalker - Programmer
- Posts: 2167
- Joined: 09 Sep 2008, 15:09
- Location: New York
- Has thanked: 5 times
- Been thanked: 40 times
Re: Programming a card
by silly freak » 20 Oct 2009, 18:59
I like eliminating redundant code - no, slightly wrong - I like redundant code getting deleted ^^
no, really, this is a good Idea. having a more uniform syntax for the different keywords makes it easier to add cards. currently, you can compare it with if every class was written in a different language. I hope you succeed
as for the planeswalkers, I'd say that a major targeting criterion is to drop the walker below his ultimate's cost
no, really, this is a good Idea. having a more uniform syntax for the different keywords makes it easier to add cards. currently, you can compare it with if every class was written in a different language. I hope you succeed
as for the planeswalkers, I'd say that a major targeting criterion is to drop the walker below his ultimate's cost
___
where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
- silly freak
- DEVELOPER
- Posts: 598
- Joined: 26 Mar 2009, 07:18
- Location: Vienna, Austria
- Has thanked: 93 times
- Been thanked: 25 times
Re: Programming a card
by cyclope » 22 Oct 2009, 18:26
OK... if i really understand, i keep the code for the second ability of Nightscape Master...
Could anybody tell me if the code for this second ability is correct...
Could anybody tell me if the code for this second ability is correct...
- cyclope
- Posts: 69
- Joined: 28 Sep 2009, 18:08
- Has thanked: 0 time
- Been thanked: 0 time
Re: Programming a card
by cyclope » 23 Oct 2009, 19:16
I 've coded some more enchantments:
Reflexes
Reflexes
- Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Reflexes"))
{
final SpellAbility spell = new Spell(card)
{
public boolean canPlayAI()
{
CardList list = new CardList(AllZone.Computer_Play.getCards());
list = list.getType("Creature");
if(list.isEmpty())
return false;
//else
CardListUtil.sortAttack(list);
CardListUtil.sortFlying(list);
for (int i=0;i<list.size();i++) {
if (CardFactoryUtil.canTarget(card, list.get(i)))
{
setTargetCard(list.get(i));
return true;
}
}
return false;
}//canPlayAI()
public void resolve()
{
PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
play.add(card);
Card c = getTargetCard();
if(AllZone.GameAction.isCardInPlay(c) && CardFactoryUtil.canTarget(card, c) )
{
card.enchantCard(c);
System.out.println("Enchanted: " +getTargetCard());
}
}//resolve()
};//SpellAbility
card.clearSpellAbility();
card.addSpellAbility(spell);
Command onEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.addExtrinsicKeyword("First Strike");
}
}//execute()
};//Command
Command onUnEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.removeExtrinsicKeyword("First Strike");
}
}//execute()
};//Command
Command onLeavesPlay = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
card.unEnchantCard(crd);
}
}
};
card.setEnchant(onEnchant);
card.setUnEnchant(onUnEnchant);
card.setLeavesPlay(onLeavesPlay);
spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************
in card.txt:
Reflexes
R
Enchantment Aura
Enchanted creature has first strike.
Enchant creature
- Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Shield of Duty and Reason"))
{
final SpellAbility spell = new Spell(card)
{
public boolean canPlayAI()
{
CardList list = new CardList(AllZone.Computer_Play.getCards());
list = list.getType("Creature");
if(list.isEmpty())
return false;
//else
CardListUtil.sortAttack(list);
CardListUtil.sortFlying(list);
for (int i=0;i<list.size();i++) {
if (CardFactoryUtil.canTarget(card, list.get(i)))
{
setTargetCard(list.get(i));
return true;
}
}
return false;
}//canPlayAI()
public void resolve()
{
PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
play.add(card);
Card c = getTargetCard();
if(AllZone.GameAction.isCardInPlay(c) && CardFactoryUtil.canTarget(card, c) )
{
card.enchantCard(c);
System.out.println("Enchanted: " +getTargetCard());
}
}//resolve()
};//SpellAbility
card.clearSpellAbility();
card.addSpellAbility(spell);
Command onEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.addExtrinsicKeyword("Protection from green");
crd.addExtrinsicKeyword("Protection from blue");
}
}//execute()
};//Command
Command onUnEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.removeExtrinsicKeyword("Protection from green");
crd.removeExtrinsicKeyword("Protection from blue");
}
}//execute()
};//Command
Command onLeavesPlay = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
card.unEnchantCard(crd);
}
}
};
card.setEnchant(onEnchant);
card.setUnEnchant(onUnEnchant);
card.setLeavesPlay(onLeavesPlay);
spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************
in card.txt:
Shield of Duty and Reason
W
Enchantment Aura
Enchanted creature has first protection from green and from blue.
Enchant creature
- Code: Select all
Scavenged Weaponry
//*************** START *********** START **************************
if(cardName.equals("Scavenged Weaponry"))
{
final SpellAbility spell = new Spell(card)
{
public boolean canPlayAI()
{
CardList list = new CardList(AllZone.Computer_Play.getCards());
list = list.getType("Creature");
if(list.isEmpty())
return false;
//else
CardListUtil.sortAttack(list);
CardListUtil.sortFlying(list);
for (int i=0;i<list.size();i++) {
if (CardFactoryUtil.canTarget(card, list.get(i)))
{
setTargetCard(list.get(i));
return true;
}
}
return false;
}//canPlayAI()
public void resolve()
{
PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
play.add(card);
Card c = getTargetCard();
if(AllZone.GameAction.isCardInPlay(c) && CardFactoryUtil.canTarget(card, c) )
{
card.enchantCard(c);
System.out.println("Enchanted: " +getTargetCard());
}
}//resolve()
};//SpellAbility
card.clearSpellAbility();
card.addSpellAbility(spell);
Command onEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.addSemiPermanentAttackBoost(1);
crd.addSemiPermanentDefenseBoost(1);
}
}//execute()
};//Command
Command onUnEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.addSemiPermanentAttackBoost(-1);
crd.addSemiPermanentDefenseBoost(-1);
}
}//execute()
};//Command
Command onLeavesPlay = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
card.unEnchantCard(crd);
}
}
};
card.setEnchant(onEnchant);
card.setUnEnchant(onUnEnchant);
card.setLeavesPlay(onLeavesPlay);
spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************
in card.txt:
Scavenged Weaponry
2 B
Enchantment Aura
Enchanted creature gets +1/+1.
Enchant creature
Cantrip
- Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Maniacal Rage"))
{
final SpellAbility spell = new Spell(card)
{
public boolean canPlayAI()
{
CardList list = new CardList(AllZone.Computer_Play.getCards());
list = list.getType("Creature");
if(list.isEmpty())
return false;
//else
CardListUtil.sortAttack(list);
CardListUtil.sortFlying(list);
for (int i=0;i<list.size();i++) {
if (CardFactoryUtil.canTarget(card, list.get(i)))
{
setTargetCard(list.get(i));
return true;
}
}
return false;
}//canPlayAI()
public void resolve()
{
PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
play.add(card);
Card c = getTargetCard();
if(AllZone.GameAction.isCardInPlay(c) && CardFactoryUtil.canTarget(card, c) )
{
card.enchantCard(c);
System.out.println("Enchanted: " +getTargetCard());
}
}//resolve()
};//SpellAbility
card.clearSpellAbility();
card.addSpellAbility(spell);
Command onEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.addSemiPermanentAttackBoost(2);
crd.addSemiPermanentDefenseBoost(2);
crd.addExtrinsicKeyword("This creature cannot block");
}
}//execute()
};//Command
Command onUnEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.addSemiPermanentAttackBoost(-2);
crd.addSemiPermanentDefenseBoost(-2);
crd.removeExtrinsicKeyword("This creature cannot block");
}
}//execute()
};//Command
Command onLeavesPlay = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
card.unEnchantCard(crd);
}
}
};
card.setEnchant(onEnchant);
card.setUnEnchant(onUnEnchant);
card.setLeavesPlay(onLeavesPlay);
spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************
in card.txt:
Maniacal Rage
1 R
Enchantment Aura
Enchanted creature gets +2/+2 and this creature cannot block.
Enchant creature
- Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Magefire Wings"))
{
final SpellAbility spell = new Spell(card)
{
public boolean canPlayAI()
{
CardList list = new CardList(AllZone.Computer_Play.getCards());
list = list.getType("Creature");
if(list.isEmpty())
return false;
//else
CardListUtil.sortAttack(list);
CardListUtil.sortFlying(list);
for (int i=0;i<list.size();i++) {
if (CardFactoryUtil.canTarget(card, list.get(i)))
{
setTargetCard(list.get(i));
return true;
}
}
return false;
}//canPlayAI()
public void resolve()
{
PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
play.add(card);
Card c = getTargetCard();
if(AllZone.GameAction.isCardInPlay(c) && CardFactoryUtil.canTarget(card, c) )
{
card.enchantCard(c);
System.out.println("Enchanted: " +getTargetCard());
}
}//resolve()
};//SpellAbility
card.clearSpellAbility();
card.addSpellAbility(spell);
Command onEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.addSemiPermanentAttackBoost(2);
crd.addExtrinsicKeyword("Flying");
}
}//execute()
};//Command
Command onUnEnchant = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
crd.addSemiPermanentAttackBoost(-2);
crd.removeExtrinsicKeyword("Flying");
}
}//execute()
};//Command
Command onLeavesPlay = new Command()
{
public void execute()
{
if (card.isEnchanting())
{
Card crd = card.getEnchanting().get(0);
card.unEnchantCard(crd);
}
}
};
card.setEnchant(onEnchant);
card.setUnEnchant(onUnEnchant);
card.setLeavesPlay(onLeavesPlay);
spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));
}//*************** END ************ END **************************
in card.txt:
Magefire Wings
U R
Enchantment Aura
Enchanted creature gets +2/+0 and has flying.
Enchant creature
- Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Sacred Nectar")
{
SpellAbility spell = new Spell(card)
{
public boolean canPlay()
{
setStackDescription(card.getName() +" - " +card.getController() +" gains 4 life.");
return super.canPlay();
}
public void resolve()
{
PlayerLife life = AllZone.GameAction.getPlayerLife(card.getController());
life.addLife(4);
}
};
spell.setDescription("You gain 4 life.");
card.clearSpellAbility();
card.addSpellAbility(spell);
}//*************** END ************ END **************************
in cards.txt:
Sacred Nectar
1 W
Sorcery
You gain 4 life.
- cyclope
- Posts: 69
- Joined: 28 Sep 2009, 18:08
- Has thanked: 0 time
- Been thanked: 0 time
Re: Programming a card
by Chris H. » 23 Oct 2009, 19:48
Thank you, Cyclope. It is nice to have some more spells added to Forge.
-
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: Programming a card
by cyclope » 25 Oct 2009, 13:49
I read the file GameActionUtil.java and i think we could add these cards:
Levitation:
Thanks for your help.
Levitation:
- Code: Select all
In GameActionUtil.java:
- in public static void executeCardStateEffects() add : Levitation.execute();
- after }// executeCardStateEffects() add:
public static Command Levitation = new Command()
{
private static final long serialVersionUID = ;
CardList gloriousAnthemList = new CardList();
public void execute()
{
String keyword = "Flying";
CardList list = gloriousAnthemList;
Card c;
// reset all cards in list - aka "old" cards
for (int i = 0; i < list.size(); i++)
{
c = list.get(i);
c.removeExtrinsicKeyword(keyword);
}
list.clear();
PlayerZone[] zone = getZone("Levitation");
for (int outer = 0; outer < zone.length; outer++)
{
CardList creature = new CardList(zone[outer].getCards());
creature = creature.getType("Creature");
for (int i = 0; i < creature.size(); i++)
{
c = creature.get(i);
if (!c.getKeyword().contains(keyword))
{
c.addExtrinsicKeyword(keyword);
gloriousAnthemList.add(c);
}
}// for inner
}// for outer
}// execute()
};
and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
static {" add: commands.put("Levitation", Levitation);
and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":
cardToEffectsList.put("Levitation", new String[] {"Levitation"});
and in cards.txt:
Levitation
2 U U
Enchantment
All creatures you control gain flying.
- Code: Select all
In GameActionUtil.java:
- in public static void executeCardStateEffects() add : Knighthood.execute();
- after }// executeCardStateEffects() add:
public static Command Levitation = new Command()
{
private static final long serialVersionUID = ;
CardList gloriousAnthemList = new CardList();
public void execute()
{
String keyword = "First Strike";
CardList list = gloriousAnthemList;
Card c;
// reset all cards in list - aka "old" cards
for (int i = 0; i < list.size(); i++)
{
c = list.get(i);
c.removeExtrinsicKeyword(keyword);
}
list.clear();
PlayerZone[] zone = getZone("Knighthood");
for (int outer = 0; outer < zone.length; outer++)
{
CardList creature = new CardList(zone[outer].getCards());
creature = creature.getType("Creature");
for (int i = 0; i < creature.size(); i++)
{
c = creature.get(i);
if (!c.getKeyword().contains(keyword))
{
c.addExtrinsicKeyword(keyword);
gloriousAnthemList.add(c);
}
}// for inner
}// for outer
}// execute()
};
and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
static {" add: commands.put("Knighthood", Knighthood);
and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":
cardToEffectsList.put("Knighthood", new String[] {"Knighthood"});
and in cards.txt:
Knighthood
2 W
Enchantment
All creatures you control gain first strike.
- Code: Select all
In GameActionUtil.java:
- in public static void executeCardStateEffects() add : Boartusk_Liege.execute();
- after }// executeCardStateEffects() add:
public static Command Boartusk_Liege = new Command()
{
private static final long serialVersionUID = ;
CardList gloriousAnthemList = new CardList();
public void execute()
{
CardList list = gloriousAnthemList;
Card c;
// reset all cards in list - aka "old" cards
for (int i = 0; i < list.size(); i++)
{
c = list.get(i);
if (CardUtil.getColors(c).contains(Constant.Color.Green)
&& !c.getName().equals("Boartusk Liege"))
{
c.addSemiPermanentAttackBoost(-1);
c.addSemiPermanentDefenseBoost(-1);
}
if (CardUtil.getColors(c).contains(Constant.Color.Red)
&& !c.getName().equals("Boartusk Liege"))
{
c.addSemiPermanentAttackBoost(-1);
c.addSemiPermanentDefenseBoost(-1);
}
}
// add +1/+1 to cards
list.clear();
PlayerZone[] zone = getZone("Boartusk Liege");
// for each zone found add +1/+1 to each card
for (int outer = 0; outer < zone.length; outer++)
{
CardList creature = new CardList(zone[outer].getCards());
creature = creature.getType("Creature");
for (int i = 0; i < creature.size(); i++)
{
c = creature.get(i);
if (CardUtil.getColors(c).contains(Constant.Color.Green)
&& !c.getName().equals("Boartusk Liege"))
{
c.addSemiPermanentAttackBoost(1);
c.addSemiPermanentDefenseBoost(1);
}
if (CardUtil.getColors(c).contains(Constant.Color.Red)
&& !c.getName().equals("Boartusk Liege"))
{
c.addSemiPermanentAttackBoost(1);
c.addSemiPermanentDefenseBoost(1);
}
gloriousAnthemList.add(c);
}// for inner
}// for outer
}// execute()
};// Boartusk_Liege
and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
static {" add: commands.put("Boartusk_Liege", Boartusk_Liege);
and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":
cardToEffectsList.put("Boartusk Liege", new String[] {"Boartusk Liege"});
and in cards.txt:
Boartusk Liege
1 RG RG RG
Creature Goblin Knight
Other green creatures you control get +1/+1. Other red creatures you control get +1/+1.
3/4
Trample
- Code: Select all
In GameActionUtil.java:
- in public static void executeCardStateEffects() add : Glen_Elendra.execute();
- after }// executeCardStateEffects() add:
public static Command Glen_Elendra = new Command()
{
private static final long serialVersionUID = ;
CardList gloriousAnthemList = new CardList();
public void execute()
{
CardList list = gloriousAnthemList;
Card c;
// reset all cards in list - aka "old" cards
for (int i = 0; i < list.size(); i++)
{
c = list.get(i);
if (CardUtil.getColors(c).contains(Constant.Color.Blue)
&& !c.getName().equals("Glen Elendra Liege"))
{
c.addSemiPermanentAttackBoost(-1);
c.addSemiPermanentDefenseBoost(-1);
}
if (CardUtil.getColors(c).contains(Constant.Color.Black)
&& !c.getName().equals("Glen Elendra Liege"))
{
c.addSemiPermanentAttackBoost(-1);
c.addSemiPermanentDefenseBoost(-1);
}
}
// add +1/+1 to cards
list.clear();
PlayerZone[] zone = getZone("Glen Elendra Liege");
// for each zone found add +1/+1 to each card
for (int outer = 0; outer < zone.length; outer++)
{
CardList creature = new CardList(zone[outer].getCards());
creature = creature.getType("Creature");
for (int i = 0; i < creature.size(); i++)
{
c = creature.get(i);
if (CardUtil.getColors(c).contains(Constant.Color.Blue)
&& !c.getName().equals("Glen Elendra Liege"))
{
c.addSemiPermanentAttackBoost(1);
c.addSemiPermanentDefenseBoost(1);
}
if (CardUtil.getColors(c).contains(Constant.Color.Black)
&& !c.getName().equals("Glen Elendra Liege"))
{
c.addSemiPermanentAttackBoost(1);
c.addSemiPermanentDefenseBoost(1);
}
gloriousAnthemList.add(c);
}// for inner
}// for outer
}// execute()
};// Glen_Elendra
and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
static {" add: commands.put("Glen_Elendra", Glen_Elendra);
and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":
cardToEffectsList.put("Glen Elendra Liege", new String[] {"Glen Elendra Liege"});
and in cards.txt:
Glen Elendra Liege
1 UB UB UB
Creature Goblin Knight
Other blue creatures you control get +1/+1. Other black creatures you control get +1/+1.
2/3
Flying
- Code: Select all
In GameActionUtil.java:
- in public static void executeCardStateEffects() add : Thistledown_Liege.execute();
- after }// executeCardStateEffects() add:
public static Command Thistledown_Liege= new Command()
{
private static final long serialVersionUID = ;
CardList gloriousAnthemList = new CardList();
public void execute()
{
CardList list = gloriousAnthemList;
Card c;
// reset all cards in list - aka "old" cards
for (int i = 0; i < list.size(); i++)
{
c = list.get(i);
if (CardUtil.getColors(c).contains(Constant.Color.Blue)
&& !c.getName().equals("Thistledown Liege"))
{
c.addSemiPermanentAttackBoost(-1);
c.addSemiPermanentDefenseBoost(-1);
}
if (CardUtil.getColors(c).contains(Constant.Color.White)
&& !c.getName().equals("Thistledown Liege"))
{
c.addSemiPermanentAttackBoost(-1);
c.addSemiPermanentDefenseBoost(-1);
}
}
// add +1/+1 to cards
list.clear();
PlayerZone[] zone = getZone("Thistledown Liege");
// for each zone found add +1/+1 to each card
for (int outer = 0; outer < zone.length; outer++)
{
CardList creature = new CardList(zone[outer].getCards());
creature = creature.getType("Creature");
for (int i = 0; i < creature.size(); i++)
{
c = creature.get(i);
if (CardUtil.getColors(c).contains(Constant.Color.Blue)
&& !c.getName().equals("Thistledown Liege"))
{
c.addSemiPermanentAttackBoost(1);
c.addSemiPermanentDefenseBoost(1);
}
if (CardUtil.getColors(c).contains(Constant.Color.White)
&& !c.getName().equals("Thistledown Liege"))
{
c.addSemiPermanentAttackBoost(1);
c.addSemiPermanentDefenseBoost(1);
}
gloriousAnthemList.add(c);
}// for inner
}// for outer
}// execute()
};// Thistledown_Liege
and in" public static HashMap<String, Command> commands = new HashMap<String, Command>();
static {" add: commands.put("Thistledown_Liege", Thistledown_Liege);
and finally in StateBasedEffects.java, after "public void initStateBasedEffectsList()":
cardToEffectsList.put("Thistledown Liege", new String[] {"Thistledown Liege"});
and in cards.txt:
Thistledown Liege
1 UB UB UB
Creature Goblin Knight
Other blue creatures you control get +1/+1. Other white creatures you control get +1/+1.
1/3
Flash
Thanks for your help.
- cyclope
- Posts: 69
- Joined: 28 Sep 2009, 18:08
- Has thanked: 0 time
- Been thanked: 0 time
Re: Programming a card
by Marek14 » 25 Oct 2009, 16:45
How about Creakwood Liege? It could be cobbled from your code and Bringer of the Green Dawn. Mindwrack Liege might also be possible, by modifying Elvish Piper.
Ashenmoor Liege, Wilt-Leaf Liege and Murkfiend Liege have more complex rules.
As for Balefire Liege and Deathbringer Liege, they should be possible (since there's Fable of Wolf and Owl, Quirion Dryad, and Merrow Levitator). With those two, the whole cycle of Hatchlings might be easy to do as well:
Belligerent Hatchling
Noxious Hatchling
Shrewd Hatchling (maybe not this one)
Sturdy Hatchling
Voracious Hatchling
Going through cards.txt, I see that there is large amount of cards implemented that trigger on you playing a certain type of spell. Further possibilities are:
Eidolon cycle: Aurora Eidolon (damage prevention, so probably not doable right now), Enigma Eidolon, Entropic Eidolon, Sandstorm Eidolon, Verdant Eidolon
Spiritcraft cards: Baku Altar, Blademane Baku, Bounteous Kirin, Briarknit Kami, Budoka Pupil (flip), Callow Jushi (flip), Celestial Kirin, Cloudhoof Kirin, Cunning Bandit (flip), Dreamcatcher, Earthshaker, Elder Pine of Jukai (soulshift), Faithful Squire (flip), Fiddlehead Kami, Guardian of Solitude, Haru-Onna, Hikari, Twilight Guardian (blink), Hired Muscle (flip), Horizon Seed, Infernal Kirin, Innocence Kami, Jade Idol, Kami of Fire's Roar, Kami of Tattered Shoji, Kami of the Hunt, Kami of the Painted Road, Kami of the Waning Moon, Kemuri-Onna, Kiri-Onna, Kodama of the South Tree, Kyoki, Sanity's Eclipse, Loam Dweller, Nikko-Onna, Orbweaver Kumo, Ore Gorger, Oyobi, Who Split the Heavens, Petalmane Baku, Quillmane Baku, Scaled Hulk, Sire of the Storm, Skullmane Baku, Skyfire Kirin, Soilshaper, Soul of Magma, Tallowisp, Teller of Tales, Thief of Hope (soulshift), Waxmane Baku, Yuki-Onna
Other cards from Merrow Levitator cycle: Ballynock Trapper, Cinder Pyromancer, Merrow Bonegnawer, Nettle Sentinel
Mimic cycle: Battlegate Mimic, Nightsky Mimic, Riverfall Mimic, Shorecrasher Mimic. Woodlurker Mimic
Duo cycle: Emberstrike Duo, Gravelgill Duo, Safehold Duo, Tattermunge Duo. Thistledown Duo
Battlewand Oak
Blood Funnel
Celestial Ancient
Circu, Dimir Lobotomist
Cloven Casting (spell copying)
Contemplation
Customs Depot
Door of Destinies
Dreamspoiler Witches
Dwarven Patrol
Endrek Sahr, Master Breeder (state trigger)
Equilibrium
Eyes of the Watcher (scry)
Faerie Tauntings
Furious Assault
Gelectrode
Glen Elendra Pranksters
Glimpse of Nature (probably hard)
Gloryscale Viashino
Halcyon Glaze
Inspired Sprite
Kurgadon
Leering Emblem
Leonin Battlemage
Leyline of Lightning (leyline ability)
Loyal Gyrfalcon
Lys Alana Bowmaster
Lys Alana Huntmaster
Manaplasm
Masked Admirers
Merrow Reejerey
Mindmoil
Mirari (spell copying)
Mishra, Artificer Prodigy
Momir Vig, Simic Visionary
Mountain Titan (hard)
Onslaught
Pristine Angel
Pyromancer Ascension (spell copying)
Quest for the Holy Relic
Spellweaver Volute (spell copying)
Thorntooth Witch
Tibor and Lumia
Tidespout Tyrant
Veilstone Amulet (beware, it doesn't give untargetability, it sets up a rule)
Voidmage Husher (ability countering)
Wee Dragonauts
Witch-Maw Nephilim
Ashenmoor Liege, Wilt-Leaf Liege and Murkfiend Liege have more complex rules.
As for Balefire Liege and Deathbringer Liege, they should be possible (since there's Fable of Wolf and Owl, Quirion Dryad, and Merrow Levitator). With those two, the whole cycle of Hatchlings might be easy to do as well:
Belligerent Hatchling
Noxious Hatchling
Shrewd Hatchling (maybe not this one)
Sturdy Hatchling
Voracious Hatchling
Going through cards.txt, I see that there is large amount of cards implemented that trigger on you playing a certain type of spell. Further possibilities are:
Eidolon cycle: Aurora Eidolon (damage prevention, so probably not doable right now), Enigma Eidolon, Entropic Eidolon, Sandstorm Eidolon, Verdant Eidolon
Spiritcraft cards: Baku Altar, Blademane Baku, Bounteous Kirin, Briarknit Kami, Budoka Pupil (flip), Callow Jushi (flip), Celestial Kirin, Cloudhoof Kirin, Cunning Bandit (flip), Dreamcatcher, Earthshaker, Elder Pine of Jukai (soulshift), Faithful Squire (flip), Fiddlehead Kami, Guardian of Solitude, Haru-Onna, Hikari, Twilight Guardian (blink), Hired Muscle (flip), Horizon Seed, Infernal Kirin, Innocence Kami, Jade Idol, Kami of Fire's Roar, Kami of Tattered Shoji, Kami of the Hunt, Kami of the Painted Road, Kami of the Waning Moon, Kemuri-Onna, Kiri-Onna, Kodama of the South Tree, Kyoki, Sanity's Eclipse, Loam Dweller, Nikko-Onna, Orbweaver Kumo, Ore Gorger, Oyobi, Who Split the Heavens, Petalmane Baku, Quillmane Baku, Scaled Hulk, Sire of the Storm, Skullmane Baku, Skyfire Kirin, Soilshaper, Soul of Magma, Tallowisp, Teller of Tales, Thief of Hope (soulshift), Waxmane Baku, Yuki-Onna
Other cards from Merrow Levitator cycle: Ballynock Trapper, Cinder Pyromancer, Merrow Bonegnawer, Nettle Sentinel
Mimic cycle: Battlegate Mimic, Nightsky Mimic, Riverfall Mimic, Shorecrasher Mimic. Woodlurker Mimic
Duo cycle: Emberstrike Duo, Gravelgill Duo, Safehold Duo, Tattermunge Duo. Thistledown Duo
Battlewand Oak
Blood Funnel
Celestial Ancient
Circu, Dimir Lobotomist
Cloven Casting (spell copying)
Contemplation
Customs Depot
Door of Destinies
Dreamspoiler Witches
Dwarven Patrol
Endrek Sahr, Master Breeder (state trigger)
Equilibrium
Eyes of the Watcher (scry)
Faerie Tauntings
Furious Assault
Gelectrode
Glen Elendra Pranksters
Glimpse of Nature (probably hard)
Gloryscale Viashino
Halcyon Glaze
Inspired Sprite
Kurgadon
Leering Emblem
Leonin Battlemage
Leyline of Lightning (leyline ability)
Loyal Gyrfalcon
Lys Alana Bowmaster
Lys Alana Huntmaster
Manaplasm
Masked Admirers
Merrow Reejerey
Mindmoil
Mirari (spell copying)
Mishra, Artificer Prodigy
Momir Vig, Simic Visionary
Mountain Titan (hard)
Onslaught
Pristine Angel
Pyromancer Ascension (spell copying)
Quest for the Holy Relic
Spellweaver Volute (spell copying)
Thorntooth Witch
Tibor and Lumia
Tidespout Tyrant
Veilstone Amulet (beware, it doesn't give untargetability, it sets up a rule)
Voidmage Husher (ability countering)
Wee Dragonauts
Witch-Maw Nephilim
Re: Programming a card
by DennisBergkamp » 25 Oct 2009, 16:50
Cyclope,
Hmm, Knighthood and Levitation should work fine... however, the Lieges are much trickier than that, since they pump "Other" creatures of the same colors.
Your code might've worked if it was for a Legendary creature, but since there could be multiple Lieges in play, it's trickier.
Hmm, Knighthood and Levitation should work fine... however, the Lieges are much trickier than that, since they pump "Other" creatures of the same colors.
Your code might've worked if it was for a Legendary creature, but since there could be multiple Lieges in play, it's trickier.
-
DennisBergkamp - AI Programmer
- Posts: 2602
- Joined: 09 Sep 2008, 15:46
- Has thanked: 0 time
- Been thanked: 0 time
Re: Programming a card
by cyclope » 25 Oct 2009, 19:38
Ok Dennis , i haven't thought about that it could have in play more than one of these Liege...
Have you got any idea to implement these cards ?
I also try to code Stormscape Apprentice:
Have you got any idea to implement these cards ?
I also try to code Stormscape Apprentice:
- Code: Select all
//*************** START *********** START **************************
if (cardName.equals("Stormscape Apprentice")
{
final SpellAbility ability = new Ability_Tap(card, "W")
{
private static final long serialVersionUID = ;
public void resolve()
{
Card c = getTargetCard();
c.tap();
}
public boolean canPlayAI() {return false;}
};//SpellAbility
final SpellAbility ability2 = new Ability_Tap(card, "B")
{
private static final long serialVersionUID = ;
public void resolve()
{
String opponent = AllZone.GameAction.getOpponent(card.getController());
AllZone.GameAction.getPlayerLife(opponent).subtractLife(1);
}
public boolean canPlayAI()
{
//computer should play ability if this creature doesn't attack
Combat c = ComputerUtil.getAttackers();
CardList list = new CardList(c.getAttackers());
//could this creature attack?, if attacks, do not use ability
return (! list.contains(card));
}
};//SpellAbility
card.addSpellAbility(ability);
ability.setDescription("W, tap: Tap target creature.");
ability.setBeforePayMana(CardFactoryUtil.input_targetCreature(ability));
card.addSpellAbility(ability2);
ability2.setDescription("B, tap: Target player loses 1 life.");
ability2.setStackDescription(card.getName() + " - Opponent loses 1 life.");
}//*************** END ************ END **************************
- Code: Select all
//*************** START *********** START **************************
if (cardName.equals("Stormscape Master")
{
final SpellAbility ability = new Ability_Tap(card, "B B")
{
private static final long serialVersionUID = ;
public void resolve()
{
String opponent = AllZone.GameAction.getOpponent(card.getController());
AllZone.GameAction.getPlayerLife(opponent).subtractLife(2);
AllZone.GameAction.getPlayerLife(card.getController()).addLife(2);
}
public boolean canPlayAI()
{
//computer should play ability if this creature doesn't attack
Combat c = ComputerUtil.getAttackers();
CardList list = new CardList(c.getAttackers());
//could this creature attack?, if attacks, do not use ability
return (! list.contains(card));
}
};//SpellAbility
card.addSpellAbility(ability);
ability.setDescription("B B, tap: Target player loses 2 life and you gain 2 life");
ability2.setStackDescription(card.getName() + " - Opponent loses 2 life."+card.getName()+card.getController()+" gains 2 life");
}//*************** END ************ END **************************
and in cards.txt:
Stormscape Master
2 U U
Creature Human Wizard
WW, tap : target creature gains protection from color of your choice until end of turn. BB, tap: target player loses 2 life and you gain 2 life.
2/2
TgtKpump W W T:Protection from white
TgtKpump W W T:Protection from red
TgtKpump W W T:Protection from black
TgtKpump W W T:Protection from green
TgtKpump W W T:Protection from blue
Last edited by cyclope on 25 Oct 2009, 20:28, edited 1 time in total.
- cyclope
- Posts: 69
- Joined: 28 Sep 2009, 18:08
- Has thanked: 0 time
- Been thanked: 0 time
Re: Programming a card
by DennisBergkamp » 25 Oct 2009, 20:18
-
DennisBergkamp - AI Programmer
- Posts: 2602
- Joined: 09 Sep 2008, 15:46
- Has thanked: 0 time
- Been thanked: 0 time
Re: Programming a card
by DennisBergkamp » 25 Oct 2009, 20:43
Oh, and actually I don't really "execute" these effects anymore with "Levitation.execute()", instead add it to the big list at the bottom of GameActionUtil:
- Code: Select all
commands.put("Levitation", Levitation);
- Code: Select all
cardToEffectsList.put("Levitation",new String[] {"Levitation"});
-
DennisBergkamp - AI Programmer
- Posts: 2602
- Joined: 09 Sep 2008, 15:46
- Has thanked: 0 time
- Been thanked: 0 time
Who is online
Users browsing this forum: No registered users and 13 guests