It is currently 17 Apr 2021, 16:44
   
Text Size

Card Development - talk about cards code here

Discuss Upcoming Releases, Coding New Cards, Etc.
PLEASE DO NOT REPORT BUGS HERE!

Moderators: BAgate, drool66, stassy, Aswan jaguar, gmzombie, CCGHQ Admins

Card Development - talk about cards code here

Postby Aswan jaguar » 08 Aug 2020, 15:45

I made Orcish Librarian and mostly works right but it needs certainly some adjustments or changes in the loop ( if this is the correct loop to begin with ).
Below code will fail if cards in deck are less than 8, as it will exile only cards above the last 4 and never the last 4,3,2,1.
So, I really need your help to finish this.
DONE

Code: Select all
int card_orcish_librarian(int player, int card, event_t event){
/* Orcish Librarian   |1|R   0x000000
 * Creature - Orc 1/1
 * |R, |T: Look at the top eight cards of your library. Exile four of them at random, then put the rest on top of your library in any order. */
 
    if( event == EVENT_RESOLVE_ACTIVATION ){
      if( count_deck(player) ){
         int amount = MIN(8, count_deck(player));
            show_deck( player, deck_ptr[player], amount, "Here's the top eight cards of your deck.", 0, 0x7375B0 );
         
          int count_g = amount;
          int cards = 0;
          int rnd_ex = MIN(4, amount);
          while( cards < 4 && count_g > 0){
               int rnd = count_g - (rnd_ex +1);
               if( rnd > cards ){
                  rnd = internal_rand(count_g);
               }
               rfg_card_in_deck(player, rnd);      
               cards++;
               count_g--;
           }
            rearrange_top_x(player, player, amount-rnd_ex);
         }
   }
   return generic_activated_ability(player, card, event, GAA_UNTAPPED, MANACOST_R(1), 0, NULL, NULL);
}
Last edited by Aswan jaguar on 12 Nov 2020, 15:24, edited 1 time in total.
Reason: strikethrough fixed
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7425
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 343 times

Re: Card Development - talk about cards code here

Postby FastEddie » 09 Aug 2020, 15:35

I had a quick try with a deck with only 4 cards left. So

amount = 4,
count_g = 4,
rnd_ex = 4.

But then rnd = 4 - (4 + 1) = -1. Same holds when you start with 5 cards or more, you end up with a -1 eventually. In these cases rfg_card_in_deck does nothing.

This code works for 4 (or less) cards. I didn't test 5 to 8, so if you have a test case handy pls give it a try.

Code: Select all
int card_orcish_librarian(int player, int card, event_t event){
/* Orcish Librarian   |1|R   0x000000
 * Creature - Orc 1/1
 * |R, |T: Look at the top eight cards of your library. Exile four of them at random, then put the rest on top of your library in any order. */
 
   if( event == EVENT_RESOLVE_ACTIVATION ){
      if( count_deck(player) ){
         int amount = MIN(8, count_deck(player));
         if (amount == 8 ) {
            show_deck( player, deck_ptr[player], amount, "Here's the top eight cards of your deck.", 0, 0x7375B0 );

            int count_g = amount;
            int cards = 0;
            int rnd_ex = MIN(4, amount);
            while( cards < 4 && count_g > 0){
               int rnd = count_g - (rnd_ex +1);
               if( rnd > cards ){
                  rnd = internal_rand(count_g);
               }
               rfg_card_in_deck(player, rnd);
               cards++;
               count_g--;
            }
            rearrange_top_x(player, player, amount-rnd_ex);
         } else {
            char buffer[100];
            int pos = scnprintf(buffer, 100, "Here's the top ");
            switch (amount) {
               case 1: pos+=scnprintf(buffer+pos, 100-pos, "one card"); break;
               case 2: pos+=scnprintf(buffer+pos, 100-pos, "two cards"); break;
               case 3: pos+=scnprintf(buffer+pos, 100-pos, "three cards"); break;
               case 4: pos+=scnprintf(buffer+pos, 100-pos, "four cards"); break;
               case 5: pos+=scnprintf(buffer+pos, 100-pos, "five cards"); break;
               case 6: pos+=scnprintf(buffer+pos, 100-pos, "six cards"); break;
               case 7: pos+=scnprintf(buffer+pos, 100-pos, "seven cards"); break;
            }
            pos+=scnprintf(buffer+pos, 100-pos, " of your deck.");

            show_deck( player, deck_ptr[player], amount, buffer, 0, 0x7375B0 );
            if (amount <= 4) {
               // Remove remaining deck
               int count;
               for (count = 0; count < amount; count++) {
                  rfg_card_in_deck(player, 0);
               }
            } else {
               int remain = amount - 4;
               int count;
               for (count = 0; count < 4; count++) {
                  rfg_card_in_deck(player, internal_rand(amount));
               amount--;
               }
            rearrange_top_x(player, player, remain);
            }
         }
      }
   }
   return generic_activated_ability(player, card, event, GAA_UNTAPPED, MANACOST_R(1), 0, NULL, NULL);
}
---
Argivian Archaeologist in the Library of Leng studying the Spells of the Ancients
User avatar
FastEddie
 
Posts: 244
Joined: 24 Dec 2019, 10:59
Has thanked: 15 times
Been thanked: 19 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 09 Aug 2020, 16:04

Thanks FastEddie, it works as I guess you expected it, to do so. Is anything else that I should have done differently to be more exact with the cards rules text or code wise?
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7425
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 343 times

Re: Card Development - talk about cards code here

Postby FastEddie » 09 Aug 2020, 17:32

No, your code was good, it just didn't take account of this special case. Rule wise I found no errata stating something different, so to me it's ok to exile up to 4 cards and rearrange the rest if less than 8 cards are left. I personally prefer more speaking (some might say talkative ;) ) variable names as it makes it easier for me to read my own code after a while without wondering what exactly the programmer thought. One could probably shorten the whole thing with clever usage of min and max functions but I think this would make it less readable and there is probably no advantage in terms of execution time.
---
Argivian Archaeologist in the Library of Leng studying the Spells of the Ancients
User avatar
FastEddie
 
Posts: 244
Joined: 24 Dec 2019, 10:59
Has thanked: 15 times
Been thanked: 19 times

Breath of Dreams - DONE

Postby Aswan jaguar » 06 Oct 2020, 17:52

I want some help with Breath of Dreams guys, both abilities work on their own but not when both are added. If both are present the abilities get messed up causing crash. DONE
Code: Select all
static void effect_breath_dream(int player, int card, event_t event, kill_t kill_mode)
{
  upkeep_trigger_ability(player, card, event, ANYBODY);

  if (event == EVENT_UPKEEP_TRIGGER_ABILITY)
   {
     int c, p = current_turn;
     char marked[151] = {0};
     for (c = 0; c < active_cards_count[p]; ++c)
      if (in_play(p, c) && is_what(p, c, TYPE_CREATURE))
        marked[c] = 1;

     card_instance_t* inst;
                       test_definition_t this_test;
      default_test_definition(&this_test, TYPE_CREATURE);
      this_test.color = get_sleighted_color_test(player, card, COLOR_TEST_GREEN);
     for (c = 0; c < active_cards_count[p]; ++c)
      if (marked[c]   // was in play and a creature at start of resolution (iterating in reverse isn't sufficient)
         && (inst = in_play(p, c))            // hasn't left play due to a trigger from a previously-unpaid upkeep
         && !(inst->token_status & STATUS_DYING)   // hasn't been marked dying by a trigger from a previously-unpaid upkeep
         && is_what(p, c, TYPE_CREATURE)         // hasn't stopped being a creature due to a trigger from a previously-unpaid upkeep
         && new_make_test_in_play(p, c, -1, &this_test)
         && (!has_mana(p, COLOR_COLORLESS, 1)
            || cumulative_upkeep_arbitrary(player, card, p, c, event, MANACOST_X(1))))
        kill_card(p, c, kill_mode);
   }
}

int card_breath_of_dreams(int player, int card, event_t event){
   /* Breath of Dreams   |2|U|U   0x000000
    * Enchantment
    * Cumulative upkeep |U
    * |S Green creatures have "Cumulative upkeep |1." */

      //cumulative_upkeep(player, card, event, MANACOST_U(1));

   effect_breath_dream(player, card, event, KILL_SACRIFICE);

   return global_enchantment(player, card, event);
}
Last edited by Aswan jaguar on 12 Nov 2020, 15:29, edited 2 times in total.
Reason: strikethrough fixed
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7425
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 343 times

Balduvian Shaman - DONE

Postby Aswan jaguar » 06 Oct 2020, 17:59

For Balduvian Shaman I have no idea how to make it check "that doesn't have cumulative upkeep". It needs a new EVENT for this? the rest works.

Code: Select all
static int doesnt_have_cumulative_upkeep(int iid, int me, int player, int card, event_t event){//not checking upkeep - activates
     // ?
   return 0;
}

static int effect_basha(int player, int card, event_t event){

   if( get_card_instance(player, card)->damage_target_player > -1 ){
      card_instance_t *instance = get_card_instance(player, card);
      int p = instance->damage_target_player;
      int c = instance->damage_target_card;

      cumulative_upkeep_arbitrary(player, card, p, c, event, MANACOST_X(1));
   }

   return 0;
}

int card_balduvian_shaman(int player, int card, event_t event){//in progress
   /* Balduvian Shaman   |U   0x000000
    * Creature - Human Cleric Shaman 1/1
    * |T: Change the text of target |Swhite enchantment you control that doesn't have cumulative upkeep by replacing all instances of one color word with another.
      That enchantment gains "Cumulative upkeep |1." */

    target_definition_t td;
   default_target_definition(player, card, &td, TYPE_ENCHANTMENT );
   td.required_color = get_sleighted_color_test(player, card, COLOR_TEST_WHITE);
   td.allowed_controller = td.preferred_controller = player;
   td.extra = (int)doesnt_have_cumulative_upkeep;
   td.special = TARGET_SPECIAL_EXTRA_FUNCTION;

   card_instance_t *instance = get_card_instance( player, card);

   if( event == EVENT_RESOLVE_ACTIVATION ){
      if( valid_target(&td) ){
         replace_all_instances_of_one_color_word_with_another(player, card, instance->targets[0].player, instance->targets[0].card);
         create_targetted_legacy_effect(instance->parent_controller, instance->parent_card, &effect_basha, instance->targets[0].player, instance->targets[0].card);
      }
   }

    return generic_activated_ability(player, card, event, GAA_UNTAPPED | GAA_CAN_TARGET | GAA_LITERAL_PROMPT, MANACOST0, 0, &td,
                                    get_sleighted_color_text(player, card, "Select target %s ench/ment without cumulative upkeep you control.", COLOR_WHITE));
 }
Last edited by Aswan jaguar on 27 Nov 2020, 17:21, edited 1 time in total.
Reason: done
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7425
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 343 times

Exploding Borders - DONE

Postby Aswan jaguar » 18 Oct 2020, 13:21

This is on of the cards coded in the past but never got in release. The issue is I can't find a way to make it count the land that it put onto the battlefield with the first ability to count for it's second ability total damage.
Can we force recalculation of lands in play after domain ability and before calculating basic land types for damage?
DONE
Code: Select all
int card_exploding_borders(int player, int card, event_t event){//UNUSEDCARD
   /* CARD_ID_EXPLODING_BORDERS   10140
   Exploding Borders   |2|R|G
   Sorcery
   Domain - Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle your library.
   ~ deals X damage to target player, where X is the number of basic land types among lands you control. */

   if( ! IS_GS_EVENT(player, card, event) ){
      return 0;
   }

   target_definition_t td1;
   default_target_definition(player, card, &td1, TYPE_CREATURE);
   td1.zone = TARGET_ZONE_PLAYERS;
   td1.allow_cancel = 0;

   card_instance_t *instance = get_card_instance( player, card );

   if(event == EVENT_RESOLVE_SPELL ){
      if( valid_target(&td1) ){
         tutor_basic_land(player, 1, 1);
         int damage = count_domain(player, card);
         if( damage > 0 ){
            damage_player(instance->targets[0].player, damage, player, card);
         }
      }
      kill_card(player, card, KILL_DESTROY);
   }

   return generic_spell(player, card, event, GS_CAN_TARGET, &td1, "TARGET_PLAYER", 1, NULL);
}
Last edited by Aswan jaguar on 12 Nov 2020, 15:28, edited 2 times in total.
Reason: strikethrough fixed
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7425
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 343 times

Re: Card Development - talk about cards code here

Postby drool66 » 30 Oct 2020, 07:19

For Balduvian Shaman I have no idea how to make it check "that doesn't have cumulative upkeep".
The only way I can think of is the worst way possible - to check by csvid, then to check if there is a card like Mana Chains or Balduvian Shaman's legacy attached to something. Not hard to do - only about 90 cards are involved, but crummy.
I can't find a way to make it count the land that it put onto the battlefield with the first ability
Domain won't work here. It uses basiclandtypes_controlled[], which is only set during recalculate_all_cards_in_play(), so unless you want to run that in the middle of resolution, you can probably just do a regular CYCLE_ALL_CARDS
User avatar
drool66
 
Posts: 604
Joined: 25 Nov 2010, 22:38
Has thanked: 147 times
Been thanked: 132 times

Re: Card Development - talk about cards code here

Postby drool66 » 30 Oct 2020, 07:35

I want some help with Breath of Dreams guys, both abilities work on their own but not when both are added. If both are present the abilities get messed up causing crash.
More than one way to do this, but I believe you need to consolidate the triggers in some way. What I would do is take "upkeep_trigger_ability" from the breath_of_dreams_effect and keep it set to "anybody", then for the combined "if( event == EVENT_UPKEEP_TRIGGER )" section, copy the code from events.c->cumulative_upkeep_arbitrary(), and set that part off with "if( current_turn == player ){" and then close it off and continue with the rest of the breath_of_dreams_effect section. So it looks like (conceptually):
Code: Select all
  upkeep_trigger_ability(player, card, event, ANYBODY);
  if (event == EVENT_UPKEEP_TRIGGER_ABILITY){
    if( current_turn == player ){
      lines 190-204 of upkeep.c, with proper arguments filled in
    }
    rest of breath_of_dreams effect code
  }
  return global_enchantment()
}
User avatar
drool66
 
Posts: 604
Joined: 25 Nov 2010, 22:38
Has thanked: 147 times
Been thanked: 132 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 31 Oct 2020, 11:14

Thanks drool66, I have done Breath of Dreams as you suggested and now it works.
For Exploding Borders I went with recalculate_all_cards_in_play(). I tried firstly with your other suggestion to use cycle_all_cards() but I didn't manage to do it that way. However it is a sorcery and it will only check recalculate_all_cards_in_play() while it resolves so not going to be an issue of slowing down game like it does with EVENTs that are checked continually, right?

EDIT: For Balduvian Shaman I tried to import shandalar's EVENT_QUERRY_CUMULATIVE_UPKEEP (commit 4845b11) which didn't seem to be difficult to adopt but proved too much for me.
Last edited by Aswan jaguar on 31 Oct 2020, 14:18, edited 2 times in total.
Reason: add edit
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7425
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 343 times

Re: Card Development - talk about cards code here

Postby drool66 » 31 Oct 2020, 17:03

For Exploding Borders I went with recalculate_all_cards_in_play()
So you probably shouldn't do that but, it did make me realize something. basiclandtypes_controlled[ ] is actually set in count_colors_of_lands_in_play(), which is then called from recalculate_all_cards_in_play(). It would make a lot more sense to run count_colors_of_lands_in_play() instead; in fact, this is probably the "proper" way to do it.

For Balduvian Shaman I tried to import shandalar's EVENT_QUERRY_CUMULATIVE_UPKEEP (commit 4845b11) which didn't seem to be difficult to adopt but proved too much for me.
I think I can pull it off, only because I figured the events system out for Baral et al. I'll put it on my list for the update after this coming one.
User avatar
drool66
 
Posts: 604
Joined: 25 Nov 2010, 22:38
Has thanked: 147 times
Been thanked: 132 times

Chaos Moon - DONE

Postby Aswan jaguar » 08 Nov 2020, 14:02

I have made Chaos Moon. It uses 2 effect cards for each of it's mana changing effects and 2 effects for the p/t change on creatures ( through pump_creatures_until_eot() ). It seems to be almost as good as the cards I took code from. One issue that is apparent if you put in play a red or more creatures while Chaos Moon is in play is that the small card on those creatures won't display it's image but a "random" .pic I believe from CardArt file. As steps or couple of turns pass the "random" .pic will change 2,3 times and then it will load it's normal card art.
And I don't have the slightest idea what causes this.
DONE
Code: Select all
int chaos_moon_legacy(int player, int card, event_t event){

   if( event == EVENT_CAST_SPELL && is_what(affected_card_controller, affected_card, TYPE_LAND) &&
       has_subtype(affected_card_controller, affected_card, get_hacked_subtype(player, card, SUBTYPE_MOUNTAIN)) )
      set_special_flags2(affected_card_controller, affected_card, SF2_CONTAMINATION);

   if( event == EVENT_COUNT_MANA ){
      int p;
      for(p=0;p<=1;p++){
         color_t clr;
         for (clr = COLOR_COLORLESS; clr <= COLOR_ARTIFACT; ++clr){
            mana_color_override[p][clr] = COLOR_COLORLESS;
         }
         int c;
         for(c=0; c<active_cards_count[p]; c++){
            if( in_play(p, c) && is_what(p, c, TYPE_LAND) && has_subtype(p, c, get_hacked_subtype(player, card, SUBTYPE_MOUNTAIN)) ){
               set_special_flags2(p, c, SF2_CONTAMINATION);
            }
         }
      }
   }
      
   if( eot_trigger(player, card, event) ){
      kill_card(player, card, KILL_EXILE);
      cant_be_responded_to = 1;
   }
      return 0;
}

int chaos_moon_legacy_odd(int player, int card, event_t event){
   if ((event == EVENT_COUNT_MANA || event == EVENT_TAP_CARD) && is_what(affected_card_controller, affected_card, TYPE_LAND)
      && has_subtype(affected_card_controller, affected_card, get_hacked_subtype(player, card, SUBTYPE_MOUNTAIN))){
      // See comments in card_mana_flare().

      if (!in_play(player, card)){
         return 0;
      }

      if (event == EVENT_COUNT_MANA){
         if (is_tapped(affected_card_controller, affected_card) || is_animated_and_sick(affected_card_controller, affected_card)
            || !can_produce_mana(affected_card_controller, affected_card) ){
            return 0;
         }

         declare_mana_available(affected_card_controller, COLOR_RED, 1);
      } else {   // event == EVENT_TAP_CARD
         if (tapped_for_mana_color >= 0){
            /* Triggers even if the land produced no mana so long as it was tapped for a mana ability (such as a Tolarian Academy that somehow became a
             * forest too, with no artifacts in play), by analogy with the ruling for Overabundance.  Differs from Mana Flare since that "adds one
             * mana... of any type that land produced". */
            produce_mana(affected_card_controller, COLOR_RED, 1);
         }
      }
   }
      
    if( eot_trigger(player, card, event) ){
      kill_card(player, card, KILL_EXILE);
      cant_be_responded_to = 1;
   }
   return 0;
}

int card_chaos_moon(int player, int card, event_t event){
   /* Chaos Moon   |3|R   0x000000
    * Enchantment
    * At the beginning of each upkeep, count the number of permanents. If the number is odd, until end of turn, |Sred creatures get +1/+1
    *       and whenever a player taps |Ha Mountain for mana, that player adds |R to his or her mana pool.
    * If the number is even, until end of turn, |Sred creatures get -1/-1 and if a player taps |Ha Mountain for mana,
    *      that |H2Mountain produces colorless mana instead of any other type. */

   upkeep_trigger_ability(player, card, event, ANYBODY);

   if( event == EVENT_UPKEEP_TRIGGER_ABILITY ){
     int n = count_permanents_by_type(ANYBODY, TYPE_PERMANENT);
     scanf("%d", &n);
    
     test_definition_t this_test;
     default_test_definition(&this_test, TYPE_CREATURE);
     this_test.color = get_sleighted_color_test(player, card, COLOR_TEST_RED);
     this_test.color_flag = MATCH;

     if (n%2 == 0){ // even
        pump_creatures_until_eot(player, card, ANYBODY, 2, -1, -1, 0, 0, &this_test);
        alternate_legacy_text(4, player, create_legacy_effect(player, card, &chaos_moon_legacy));
     }
      else{ // odd
          pump_creatures_until_eot(player, card, ANYBODY, 1, 1, 1, 0, 0, &this_test);
          alternate_legacy_text(3, player, create_legacy_effect(player, card, &chaos_moon_legacy_odd));
      }
   }

   return global_enchantment(player, card, event);
}
Attachments
2020-11-08_152123.jpg
Last edited by Aswan jaguar on 12 Nov 2020, 15:28, edited 2 times in total.
Reason: strikethrough fixed
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7425
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 343 times

Re: Card Development - talk about cards code here

Postby drool66 » 08 Nov 2020, 16:33

First of all, awesome idea for a card to code, and I appreciate that you're using the new mana variables.
Second, I tried your code & I do not have the same issue - the card works great. Could be a packaging issue with the patch - are you using v1.1?
User avatar
drool66
 
Posts: 604
Joined: 25 Nov 2010, 22:38
Has thanked: 147 times
Been thanked: 132 times

Re: Card Development - talk about cards code here

Postby FastEddie » 09 Nov 2020, 09:54

Nice one!

Just one comment from my side. The scanf here
Code: Select all
int n = count_permanents_by_type(ANYBODY, TYPE_PERMANENT);
scanf("%d", &n);
looks fishy and is dangerous. It reads a number the user has to enter via keyboard and would overwrite n which you just calculated. If you are running Magic.exe from a command prompt you should see something, otherwise it probably gets ignored but it is risky nevertheless.
---
Argivian Archaeologist in the Library of Leng studying the Spells of the Ancients
User avatar
FastEddie
 
Posts: 244
Joined: 24 Dec 2019, 10:59
Has thanked: 15 times
Been thanked: 19 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 09 Nov 2020, 16:22

It was
drool66 wrote:Second, I tried your code & I do not have the same issue - the card works great. Could be a packaging issue with the patch - are you using v1.1?
Yes, it was a packaging issue. And removed the scanf("%d", &n); code. Thanks, guys.
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7425
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 343 times

Next

Return to Development

Who is online

Users browsing this forum: No registered users and 1 guest


Who is online

In total there is 1 user online :: 0 registered, 0 hidden and 1 guest (based on users active over the past 10 minutes)
Most users ever online was 1371 on 09 Feb 2020, 16:22

Users browsing this forum: No registered users and 1 guest

Login Form