Card Development Questions
Post MTG Forge Related Programming Questions Here
Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins
Re: Card Development Questions
by moomarc » 24 May 2012, 05:17
Thanks ArsenalNut. That's what I had in mind as well but much more likely to actually work in your handsArsenalNut wrote:I am going to create a new "Clone" AF based on the clone code in CardFactoryCreatures.

-Marc
-
moomarc - Pixel Commander
- Posts: 2091
- Joined: 04 Jun 2010, 15:22
- Location: Johannesburg, South Africa
- Has thanked: 371 times
- Been thanked: 372 times
Re: Card Development Questions
by moomarc » 25 May 2012, 10:00
I've been working with Archangel of Strife to see what I can do to make it work.
- Archangel of Strife | Open
- Code: Select all
Name:Archangel of Strife
ManaCost:5 W W
Types:Creature Angel
Text:no text
PT:6/6
K:Flying
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ Tolstoy | Static$ True | TriggerDescription$ As CARDNAME enters the battlefield, each player chooses war or peace. Creatures controlled by players who chose war get +3/+0. Creatures controlled by players who chose peace get +0/+3.
SVar:Tolstoy:AB$ GenericChoice | Cost$ 0 | Defined$ You | Choices$ WarChoice,PeaceChoice | SubAbility$ OppChoice
SVar:OppChoice:DB$ GenericChoice | Cost$ 0 | Defined$ Opponent | Choices$ Attacking,Defensive
SVar:WarChoice:DB$ Effect | Name$ Archangel's War Effect | StaticAbilities$ War | ChoiceDescription$ War | Duration$ UntilHostLeavesPlay
SVar:PeaceChoice:DB$ Effect | Name$ Archangel's Peace Effect | StaticAbilities$ Peace | ChoiceDescription$ Peace | Duration$ UntilHostLeavesPlay
SVar:Attacking:DB$ Effect | Name$ Archangel's War Effect | StaticAbilities$ War | ChoiceDescription$ War | EffectOwner$ Opponent | Duration$ UntilHostLeavesPlay
SVar:Defensive:DB$ Effect | Name$ Archangel's Peace Effect | StaticAbilities$ Peace | ChoiceDescription$ Peace | EffectOwner$ Opponent | Duration$ UntilHostLeavesPlay
SVar:War:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ 3
SVar:Peace:Mode$ Continuous | Affected$ Creature.YouCtrl | AddToughness$ 3
SVar:RemAIDeck:True
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/archangel_of_strife.jpg
SetInfo:COM|Rare|http://magiccards.info/scans/en/cmd/7.jpg
End
-Marc
-
moomarc - Pixel Commander
- Posts: 2091
- Joined: 04 Jun 2010, 15:22
- Location: Johannesburg, South Africa
- Has thanked: 371 times
- Been thanked: 372 times
Re: Card Development Questions
by Sloth » 25 May 2012, 11:48
The Angel could have 4 continuous static abilities that check what Effects are in play.moomarc wrote:I've been working with Archangel of Strife to see what I can do to make it work.I've added the code for EffectOwner and for UntilHostLeavesPlay duration. All I need to make it work for all situations is a way to suppress the effects if the archangel loses its abilities temporarily (or permanently). I can't quite see how to get that to work though. Any ideas?
- Archangel of Strife | Open
- Code: Select all
Name:Archangel of Strife
ManaCost:5 W W
Types:Creature Angel
Text:no text
PT:6/6
K:Flying
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ Tolstoy | Static$ True | TriggerDescription$ As CARDNAME enters the battlefield, each player chooses war or peace. Creatures controlled by players who chose war get +3/+0. Creatures controlled by players who chose peace get +0/+3.
SVar:Tolstoy:AB$ GenericChoice | Cost$ 0 | Defined$ You | Choices$ WarChoice,PeaceChoice | SubAbility$ OppChoice
SVar:OppChoice:DB$ GenericChoice | Cost$ 0 | Defined$ Opponent | Choices$ Attacking,Defensive
SVar:WarChoice:DB$ Effect | Name$ Archangel's War Effect | StaticAbilities$ War | ChoiceDescription$ War | Duration$ UntilHostLeavesPlay
SVar:PeaceChoice:DB$ Effect | Name$ Archangel's Peace Effect | StaticAbilities$ Peace | ChoiceDescription$ Peace | Duration$ UntilHostLeavesPlay
SVar:Attacking:DB$ Effect | Name$ Archangel's War Effect | StaticAbilities$ War | ChoiceDescription$ War | EffectOwner$ Opponent | Duration$ UntilHostLeavesPlay
SVar:Defensive:DB$ Effect | Name$ Archangel's Peace Effect | StaticAbilities$ Peace | ChoiceDescription$ Peace | EffectOwner$ Opponent | Duration$ UntilHostLeavesPlay
SVar:War:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ 3
SVar:Peace:Mode$ Continuous | Affected$ Creature.YouCtrl | AddToughness$ 3
SVar:RemAIDeck:True
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/archangel_of_strife.jpg
SetInfo:COM|Rare|http://magiccards.info/scans/en/cmd/7.jpg
End
-
Sloth - Programmer
- Posts: 3498
- Joined: 23 Jun 2009, 19:40
- Has thanked: 125 times
- Been thanked: 507 times
Re: Card Development Questions
by moomarc » 29 May 2012, 16:44
@Hellfish (I think you're the right person to ask) : I'm trying to add Blood of the Martyr and the script works well except for one thing. If you choose not to replace the damage at any stage, you're never asked again. It will keep asking as long as you say yes to the replacement option so I'm assuming that the replacement being on an Effect card breaks the logic for optional replacement. What's the best way to fix this?
- Blood of the Martyr script | Open
- Code: Select all
Name:Blood of the Martyr
ManaCost:W W W
Types:Instant
Text:no text
A:SP$ Effect | Cost$ W W W | Name$ Blood of the Martyr Effect | ReplacementEffects$ MartyrCombat,MartyrNonCombat | SVars$ CombatDmgToYou,NonCombatDmgToYou,X | SpellDescription$ Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
SVar:MartyrCombat:Event$ DamageDone | ValidTarget$ Creature | Optional$ True | IsCombat$ True | ReplaceWith$ CombatDmgToYou | Description$ Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
SVar:MartyrNonCombat:Event$ DamageDone | ValidTarget$ Creature | Optional$ True | IsCombat$ False | ReplaceWith$ NonCombatDmgToYou | Secondary$ True | Description$ Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
SVar:CombatDmgToYou:AB$DealDamage | Cost$ 0 | Defined$ You | DamageSource$ ReplacedSource | CombatDamage$ True | NumDmg$ X
SVar:NonCombatDmgToYou:AB$DealDamage | Cost$ 0 | Defined$ You | DamageSource$ ReplacedSource | NumDmg$ X
SVar:X:ReplaceCount$DamageAmount
SVar:RemAIDeck:True
SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/blood_of_the_martyr.jpg
SetInfo:DRK|Rare|http://magiccards.info/scans/en/dk/75.jpg
SetInfo:CHR|Uncommon|http://magiccards.info/scans/en/ch/60.jpg
Oracle:Until end of turn, if damage would be dealt to any creature, you may have that damage dealt to you instead.
End
-Marc
-
moomarc - Pixel Commander
- Posts: 2091
- Joined: 04 Jun 2010, 15:22
- Location: Johannesburg, South Africa
- Has thanked: 371 times
- Been thanked: 372 times
Re: Card Development Questions
by Hellfish » 29 May 2012, 19:10
I think I've fixed that,marc. Wanna try again?
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
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
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Re: Card Development Questions
by moomarc » 29 May 2012, 19:37
I'll try again in the morning (getting a bit late on my sideHellfish wrote:I think I've fixed that,marc. Wanna try again?

-Marc
-
moomarc - Pixel Commander
- Posts: 2091
- Joined: 04 Jun 2010, 15:22
- Location: Johannesburg, South Africa
- Has thanked: 371 times
- Been thanked: 372 times
Re: Card Development Questions
by moomarc » 30 May 2012, 07:05
Looks like I'm putting replacement effects through their [cornercase] paces belatedly. The previous problem is fixed now (thanks for thatmoomarc wrote:I'll try again in the morning (getting a bit late on my sideHellfish wrote:I think I've fixed that,marc. Wanna try again?) Thanks for looking into it.

- Code: Select all
optDecider = AbilityFactory.getDefinedPlayers(replacementEffect.getHostCard(),
mapParams.get("OptionalDecider"), effectSA).get(0);
Edit: That's exactly it! Tried rescripting so that the effect had a Remembered player and OptionalDecider set to Remembered (no activating player required) and it works. Now just to fix it...
Edit2: Not sure whether this is the best solution so won't commit unless someone approves (or no-one disapproves in the next day). I've just set the Activating player in the OptionalDecider block so that the getDefinedPlayers has a reference point. I wasn't sure how the activating player is/should be handled through the rest of the replacement process so didn't want to set the AP any sooner. Regardless, I decided that for the purpose needed here it was suitable to have the reference point as the caster of the replacement effect. Here's the revised block:
- Code: Select all
if (replacementEffect.getMapParams().containsKey("Optional")) {
Player optDecider = decider;
if (mapParams.containsKey("OptionalDecider") && (effectSA != null)) {
effectSA.setActivatingPlayer(replacementEffect.getHostCard().getController());
optDecider = AbilityFactory.getDefinedPlayers(replacementEffect.getHostCard(),
mapParams.get("OptionalDecider"), effectSA).get(0);
}
// build question and AI-logic
}
-Marc
-
moomarc - Pixel Commander
- Posts: 2091
- Joined: 04 Jun 2010, 15:22
- Location: Johannesburg, South Africa
- Has thanked: 371 times
- Been thanked: 372 times
Re: Card Development Questions
by moomarc » 31 May 2012, 20:52
I've got Draco ready to commit, just need feedback on which of the two options to use:
So which should I go with? Or is neither really ideal?
Are there any other tests I should perform?
- Option 1: Long-form scripted ETB trigger | Open
- Code: Select all
K:CostChange:Player:Less:X:Self:All:All:NoSpecial:Domain - CARDNAME costs 2 less to cast for each basic land type among lands you control.
K:Flying
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ Domain - At the beginning of your upkeep, sacrifice CARDNAME unless you pay 10. This cost is reduced by 2 for each basic land type among lands you control.
SVar:TrigSac:AB$Sacrifice | Cost$ 0 | Defined$ Self | UnlessCost$ UpkeepX | UnlessPayer$ You
SVar:X:Count$Domain/Twice
SVar:UpkeepX:Number$10/Minus.X
- Code: Select all
// The cost
String unlessCost = params.get("UnlessCost").trim();
try {
String unlessVar = Integer.toString(AbilityFactory.calculateAmount(source, params.get("UnlessCost"), sa));
unlessCost = unlessVar;
} catch (final NumberFormatException n) {
}
// Old block for hardcoded X reference
/*if (unlessCost.equals("X")) {
unlessCost = Integer.toString(AbilityFactory.calculateAmount(source, params.get("UnlessCost"), sa));
}*/
- Option 2: Keyworded sac-at-upkeep-unless-you-pay | Open
- Code: Select all
K:CostChange:Player:Less:X:Self:All:All:NoSpecial:Domain - CARDNAME costs 2 less to cast for each basic land type among lands you control.
K:Flying
K:At the beginning of your upkeep, sacrifice CARDNAME unless you pay UpkeepX:Domain - At the beginning of your upkeep, sacrifice CARDNAME unless you pay 10. This cost is reduced by 2 for each basic land type among lands you control
SVar:X:Count$Domain/Twice
SVar:UpkeepX:Number$10/Minus.X
- Upkeep.java checks for UpkeepX in the "At the beginning of your upkeep, sacrifice" keyword's " pay " split, and calculates cost from the UpkeepX svar.- Code: Select all
if (ability.startsWith("At the beginning of your upkeep, sacrifice")) {
final String[] k = ability.split(" pay ");
if (k[1].contains("UpkeepX")) {
Integer upkeepX = CardFactoryUtil.xCount(c, c.getSVar("UpkeepX"));
cost = String.valueOf(upkeepX);
} else {
cost = k[1].toString();
}
sb.append("Sacrifice upkeep for ").append(c).append("\n");
}
// Old code:
if (ability.startsWith("At the beginning of your upkeep, sacrifice")) {
final String[] k = ability.split(" pay ");
cost = k[1].toString();
sb.append("Sacrifice upkeep for ").append(c).append("\n");
}
- Code: Select all
} else if (keyword.get(i).contains("At the beginning of your upkeep, ")
&& keyword.get(i).contains(" unless you pay")) {
final String[] k = keyword.get(i).split(":");
if (k.length > 1) {
sbLong.append(k[1]);
} else {
sbLong.append(k[0]);
}
sbLong.append("\r\n");
}
//Old code:
} else if (keyword.get(i).contains("At the beginning of your upkeep, ")
&& keyword.get(i).contains(" unless you pay")) {
sbLong.append(keyword.get(i).toString()).append("\r\n");
}
So which should I go with? Or is neither really ideal?
Are there any other tests I should perform?
-Marc
-
moomarc - Pixel Commander
- Posts: 2091
- Joined: 04 Jun 2010, 15:22
- Location: Johannesburg, South Africa
- Has thanked: 371 times
- Been thanked: 372 times
Re: Card Development Questions
by friarsol » 31 May 2012, 20:57
I would recommend scripting as much as you can. I'm not sure how far along the CostChange work is. If that's almost done, maybe Draco can wait for that to finish up so it looks a bit cleaner.
- friarsol
- Global Moderator
- Posts: 7593
- Joined: 15 May 2010, 04:20
- Has thanked: 243 times
- Been thanked: 965 times
Re: Card Development Questions
by moomarc » 31 May 2012, 20:59
That's what I thought. Is the way I've handled checking for valid svars fine though? Its not a method I've seen anywhere else (for this purpose at least) so I want to be sure.friarsol wrote:I would recommend scripting as much as you can. I'm not sure how far along the CostChange work is. If that's almost done, maybe Draco can wait for that to finish up so it looks a bit cleaner.
-Marc
-
moomarc - Pixel Commander
- Posts: 2091
- Joined: 04 Jun 2010, 15:22
- Location: Johannesburg, South Africa
- Has thanked: 371 times
- Been thanked: 372 times
Re: Card Development Questions
by pokey » 01 Jun 2012, 21:41
Hi, this is my first post.
I am struggling to make Dream Salvage(SHM) work, because I want to use it in Forge, and I need a help.
Is there any way to define "the number of cards target opponent discarded this turn"?
I found that "the number of cards target opponent drew this turn" was working right in game.
The card is Molten Psyche, and it has a line like this;
SVar:X:Count$OppDrewThisTurn
So I borrowed that line in my developing Dream Salvage, and it worked fine except for draw/discard conversion.
Then I tested these, but neither worked.
SVar:X:Count$OppDiscardedThisTurn
SVar:X:Count$OppDiscardThisTurn
How should I spell in that line?
Thank you in advance.
I am struggling to make Dream Salvage(SHM) work, because I want to use it in Forge, and I need a help.
Is there any way to define "the number of cards target opponent discarded this turn"?
I found that "the number of cards target opponent drew this turn" was working right in game.
The card is Molten Psyche, and it has a line like this;
SVar:X:Count$OppDrewThisTurn
So I borrowed that line in my developing Dream Salvage, and it worked fine except for draw/discard conversion.
Then I tested these, but neither worked.
SVar:X:Count$OppDiscardedThisTurn
SVar:X:Count$OppDiscardThisTurn
How should I spell in that line?
Thank you in advance.
- Dream Salvage under development | Open
- Name:Dream Salvage
ManaCost:BU
Types:Instant
Text:no text
A:SP$ Draw | Cost$ BU | NumCards$ X | SpellDescription$ (testing)Draw cards equal to the number of cards target opponent discarded this turn.
SVar:X:Count$OppDrewThisTurn
- pokey
- Posts: 4
- Joined: 25 May 2012, 22:03
- Has thanked: 0 time
- Been thanked: 0 time
Re: Card Development Questions
by ArsenalNut » 01 Jun 2012, 23:06
I made a quick investigation of the code. I didn't find any place where the number of cards discarded by a player is counted like number of cards drawn is. Without modifications to Forge, Dream Salvage cannot be scripted.pokey wrote:Hi, this is my first post.
I am struggling to make Dream Salvage(SHM) work, because I want to use it in Forge, and I need a help.
Is there any way to define "the number of cards target opponent discarded this turn"?
I found that "the number of cards target opponent drew this turn" was working right in game.
The card is Molten Psyche, and it has a line like this;
SVar:X:Count$OppDrewThisTurn
So I borrowed that line in my developing Dream Salvage, and it worked fine except for draw/discard conversion.
Then I tested these, but neither worked.
SVar:X:Count$OppDiscardedThisTurn
SVar:X:Count$OppDiscardThisTurn
How should I spell in that line?
Thank you in advance.
- Dream Salvage under development | Open
- Name:Dream Salvage
ManaCost:BU
Types:Instant
Text:no text
A:SP$ Draw | Cost$ BU | NumCards$ X | SpellDescription$ (testing)Draw cards equal to the number of cards target opponent discarded this turn.
SVar:X:Count$OppDrewThisTurn
So many cards, so little time
-
ArsenalNut - Posts: 512
- Joined: 08 Jul 2011, 03:49
- Has thanked: 27 times
- Been thanked: 121 times
Re: Card Development Questions
by pokey » 02 Jun 2012, 06:46
Thank you for your reply and investigation.ArsenalNut wrote:I made a quick investigation of the code. I didn't find any place where the number of cards discarded by a player is counted like number of cards drawn is. Without modifications to Forge, Dream Salvage cannot be scripted.
Sad to hear that.
P.S.
I made another try.
SVar:X:Count$ThisTurnEntered_Graveyard_from_Hand_Card.YouDontCtrl
This time it seems fine.
- seemingly working Dream Salvage | Open
- Name:Dream Salvage
ManaCost:BU
Types:Instant
Text:no text
A:SP$ Draw | Cost$ BU | NumCards$ X | SpellDescription$ Draw cards equal to the number of cards target opponent discarded this turn.
SVar:X:Count$ThisTurnEntered_Graveyard_from_Hand_Card.YouDontCtrl
SVar:Rarity:Uncommon
I first thought this would not work and I should do something extra to exclude cards not-discarded-but-put-in-graveyard-from-hand,
like subtracting the numbers of cards such as sorcery and instant which were normaly cast and put into graveyard from hand,
otherwise I will be drawing too much.
But to my surprise, in my test, Dream Salvage let me draw for exactly the number of discarded cards, excluding sorcery and instant opponent cast in that turn.
So let me report this finding;
When spells are cast and put in graveyard, it is not counted by "ThisTurnEntered_Graveyard_from_Hand"
Last edited by pokey on 02 Jun 2012, 07:58, edited 1 time in total.
- pokey
- Posts: 4
- Joined: 25 May 2012, 22:03
- Has thanked: 0 time
- Been thanked: 0 time
Re: Card Development Questions
by moomarc » 02 Jun 2012, 07:20
Another small point is that the card is targeted so you''ll probably end up using something like Count$TargetedDiscardedThisTurn once the suppurt is in. You can check Keeper of the Mind to see how to target the AI while you draw the cards.pokey wrote:Thank you for your reply and investigation.ArsenalNut wrote:I made a quick investigation of the code. I didn't find any place where the number of cards discarded by a player is counted like number of cards drawn is. Without modifications to Forge, Dream Salvage cannot be scripted.
Sad to hear that.
-Marc
-
moomarc - Pixel Commander
- Posts: 2091
- Joined: 04 Jun 2010, 15:22
- Location: Johannesburg, South Africa
- Has thanked: 371 times
- Been thanked: 372 times
Re: Card Development Questions
by Hellfish » 02 Jun 2012, 08:00
Correct, that is because nonpermanent spells enter the graveyard from the stack.pokey wrote:So let me report this finding;
When spells are cast and put in graveyard, it is not counted by "ThisTurnEntered_Graveyard_from_Hand"

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
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
-
Hellfish - Programmer
- Posts: 1297
- Joined: 07 Jun 2009, 10:41
- Location: South of the Pumphouse
- Has thanked: 110 times
- Been thanked: 169 times
Who is online
Users browsing this forum: No registered users and 28 guests