It is currently 23 May 2025, 15:40
   
Text Size

Card Development Questions

Post MTG Forge Related Programming Questions Here

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

Re: Card Development Questions

Postby moomarc » 24 May 2012, 05:17

ArsenalNut wrote:I am going to create a new "Clone" AF based on the clone code in CardFactoryCreatures.
Thanks ArsenalNut. That's what I had in mind as well but much more likely to actually work in your hands :mrgreen:
-Marc
User avatar
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

Postby 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
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?
-Marc
User avatar
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

Postby Sloth » 25 May 2012, 11:48

moomarc wrote: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
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?
The Angel could have 4 continuous static abilities that check what Effects are in play.
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: Card Development Questions

Postby 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
Edit: Just tried testing as an enchantment with the replacement effects and the same thing happens. It doesn't matter if you go to the next turn or whether the source/targets change... it just does nothing after that "No".
-Marc
User avatar
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

Postby 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
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: Card Development Questions

Postby moomarc » 29 May 2012, 19:37

Hellfish wrote:I think I've fixed that,marc. Wanna try again?
I'll try again in the morning (getting a bit late on my side :mrgreen: ) Thanks for looking into it.
-Marc
User avatar
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

Postby moomarc » 30 May 2012, 07:05

moomarc wrote:
Hellfish wrote:I think I've fixed that,marc. Wanna try again?
I'll try again in the morning (getting a bit late on my side :mrgreen: ) Thanks for looking into it.
Looks like I'm putting replacement effects through their [cornercase] paces belatedly. The previous problem is fixed now (thanks for that :D ) but another problem has reared it's head: when the damaged creature is the opponent's, they get to decide whether to run the replacement. So simply add the OptionalDecider param, of course. Problem is that it throws a NPE. The problem lies in this line:
Code: Select all
optDecider = AbilityFactory.getDefinedPlayers(replacementEffect.getHostCard(),
                        mapParams.get("OptionalDecider"), effectSA).get(0);
I tested by changing it to optDecider=replacementEffect.getHostCard().getController() and it worked as I should, so I think the problem lies in the fact that effectSA doesn't have an ActivatingPlayer set (my logic being that the getDefinedPlayers code needs ActivatingPlayer to determine "You")

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
User avatar
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

Postby moomarc » 31 May 2012, 20:52

I've got Draco ready to commit, just need feedback on which of the two options to use:

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
This option just needed AbilityFactory.passUnlessCost.java to accept any SVar name instead of just "X". I couldn't use X because that is also a hardcoded svar for the cost reduction. I used a try/catch to check for valid svars:
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));
        }*/
I tested Draco, Pact of the Titan, Fettergeist, Carnophage, AEther Barrier and Justice to try cover the different existing costs and all still worked properly.

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
This method requires 2 changes:
- 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");
                    }
- Card.java also needs a change to allow the card panel description to be read from a second split in the keyword.
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");
                }
I tested a bunch of the other cards using this keyword and their descriptions still displayed correctly and they functioned correctly.


So which should I go with? Or is neither really ideal?

Are there any other tests I should perform?
-Marc
User avatar
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

Postby 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

Postby moomarc » 31 May 2012, 20:59

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.
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.
-Marc
User avatar
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

Postby 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.

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

Postby ArsenalNut » 01 Jun 2012, 23:06

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
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.
So many cards, so little time
User avatar
ArsenalNut
 
Posts: 512
Joined: 08 Jul 2011, 03:49
Has thanked: 27 times
Been thanked: 121 times

Re: Card Development Questions

Postby pokey » 02 Jun 2012, 06:46

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.
Thank you for your reply and investigation.
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

Postby moomarc » 02 Jun 2012, 07:20

pokey wrote:
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.
Thank you for your reply and investigation.
Sad to hear that.
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.
-Marc
User avatar
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

Postby Hellfish » 02 Jun 2012, 08:00

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"
Correct, that is because nonpermanent spells enter the graveyard from the stack. :)
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

PreviousNext

Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 22 guests


Who is online

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

Login Form