It is currently 08 May 2021, 10:01
   
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

Re: How to make "target creature other than enchanted creatu

Postby Aswan jaguar » 25 Feb 2021, 17:04

I can't make the "target creature other than enchanted" for Kjeldoran Pride.

Code: Select all
static const char* not_the_enchanted(int who_chooses, int player, int card, int targeting_player, int targeting_card)
{
  if (  )          // what?
   return NULL;
  else
   return "can't be this enchanted creature.";
}

int card_kjeldoran_pride(int player, int card, event_t event){
   /* CARD_ID_KJELDORAN_PRIDE   2306
   Kjeldoran Pride   |1|W
   Enchantment - Aura
   Enchant creature
   Enchanted creature gets +1/+2.
   |2|U: Attach ~ to target creature other than enchanted creature. */

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

      target_definition_t td;
      default_target_definition(player, card, &td, TYPE_CREATURE);
      td.preferred_controller = player;
      td.special = TARGET_SPECIAL_EXTRA_FUNCTION;
      td.extra = (int32_t)not_the_enchanted;
      //td.special = TARGET_SPECIAL_NOT_ME; // not working as the aura is targeting.

      if( event == EVENT_CAN_ACTIVATE || event == EVENT_ACTIVATE ){
          return generic_activated_ability(player, card, event, GAA_CAN_TARGET | GAA_LITERAL_PROMPT, MANACOST_XU(2, 1), 0,
                                           &td, "Select another target creature to attach this.");
      }

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

   if (event == EVENT_CHECK_PUMP)   // will only be sent to this card if it has flash, e.g. for Mageta's Boon
      return vanilla_instant_pump(player, card, event, ANYBODY, player, 1, 2, 0, 0);

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

   return generic_aura(player, card, event, player, 1, 2, 0, 0, 0, 0, 0);
}
Also we don't have anything new for Hipparion, right?
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7436
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 345 times

Re: Card Development - talk about cards code here

Postby drool66 » 26 Feb 2021, 03:08

I don't quite have the time to do it right now, but take a look at the targeting function in Godsend from Journey into Nyx - not 100% sure, but I think your solution would be similar, just work in instance->damage_target_player/card for is_blocking(). I'll update later if you're still having trouble.
EDIT: Here's my guess:
Code: Select all
static const char* not_the_enchanted(int who_chooses, int player, int card, int targeting_player, int targeting_card)
{
  card_instance_t* kp = get_card_instance(targeting_player, targeting_card);
  if ( !(kp->damage_target_player == player && kp->damage_target_card == card) )          // what?
   return NULL;
  else
   return "can't be this enchanted creature.";
}
User avatar
drool66
 
Posts: 609
Joined: 25 Nov 2010, 22:38
Has thanked: 148 times
Been thanked: 133 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 26 Feb 2021, 19:01

It crashes like most of my tries did. A dizzy persons mistake :rolleyes: it is not crashing but not working either you can still target enchanted creature.
Last edited by Aswan jaguar on 27 Feb 2021, 09:37, edited 1 time in total.
Reason: edited my dizzy result
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7436
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 345 times

Re: Card Development - talk about cards code here

Postby drool66 » 27 Feb 2021, 12:11

It needs a general return value rather than one behind an "else". I tested and confirmed this working for me:
Code: Select all
static const char* not_the_enchanted(int who_chooses, int player, int card, int targeting_player, int targeting_card)
{
  card_instance_t* kp = get_card_instance(targeting_player, targeting_card);
  if ( !(kp->damage_target_player == player && kp->damage_target_card == card) ){
   return NULL;
  }

   return "can't be this enchanted creature.";
}
To go back to your previous post - no, I haven't worked on Hipparion. I'm amazed how subtly complex that card function is.
User avatar
drool66
 
Posts: 609
Joined: 25 Nov 2010, 22:38
Has thanked: 148 times
Been thanked: 133 times

Re: Card Development - talk about cards code here

Postby drool66 » 02 Mar 2021, 03:07

Not strictly on topic, but does anyone know why devoid cards are not tagged "Color Colorless" in ct_all? (at least the ones I checked aren't) I'm trying to get them to work with Mystic Forge without special casing "get_color_by_internal_id() == 0" for them. This wouldn't be terrible, but I feel it breaks or bends something else. I also had to modify get_color_by_internal_id() to be able to return COLOR_TEST_COLORLESS for non-artifact, non-land cards, while still stripping it out for colored cards that produce colorless mana.
User avatar
drool66
 
Posts: 609
Joined: 25 Nov 2010, 22:38
Has thanked: 148 times
Been thanked: 133 times

Re: Card Development - talk about cards code here

Postby drool66 » 28 Apr 2021, 15:07

Any ideas on how to code K'rrik, Son of Yawgmoth? I feel like I'd have to shim charge_mana() and check_mana_multi(); there has to be a better way than that.
User avatar
drool66
 
Posts: 609
Joined: 25 Nov 2010, 22:38
Has thanked: 148 times
Been thanked: 133 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 02 May 2021, 15:38

I am trying to make Sabertooth Cobra and I have two big issues (in fact both that separate the two cards :oops: ) that I tried everything I could think before I gave up frustrated. Of course my base is Nafs Asp code slightly changed for Sabertooth Cobra. The first issue is that I couldn't find how to make the effect resolve in upkeep and only then! The other was how to combine correctly the combat damage poison with the poison of the effect. The closest I came by was to have both give poison to correct players BUT the combat damage poison added a +1 counter after the first counter. The code bellow has just the 2 effects before any combination.
Code: Select all
int sabertooth_cobra_legacy(int player, int card, event_t event){

   if (event == EVENT_PHASE_CHANGED && current_phase == PHASE_DRAW && get_card_instance(player, card)->targets[1].player != 66 ){
      if( current_turn == get_card_instance(player, card)->targets[0].player ){
         int psn = 1;
         if( has_mana(current_turn, COLOR_COLORLESS, 2) ){
            charge_mana_while_resolving(player, card, EVENT_DRAW_PHASE, current_turn, COLOR_COLORLESS, 2);
            if( spell_fizzled != 1 ){
               psn = 0;
            }
         }
         if( psn ){
            poison(current_turn, 1);
         }
         get_card_instance(player, card)->targets[1].player = 66;   // prevent being asked multiple times
         kill_card(player, card, KILL_EXILE);
      }
   }

   if(event == EVENT_PHASE_CHANGED && current_phase != PHASE_DRAW){
      get_card_instance(player, card)->targets[1].player = 0;
   }

   return 0;
}

int card_sabertooth_cobra(int player, int card, event_t event){
   /* CARD_ID_SABERTOOTH_COBRA   2589
   Sabertooth Cobra   |2|G
   Creature - Snake 2/2
   Whenever ~ deals damage to a player, that player gets a poison counter. The player gets another poison counter at the beginning of their next upkeep unless
    they pay |2 before that step. (A player with ten or more poison counters loses the game.) */

   int result = damage_dealt_by_me(player, card, event, DDBM_MUST_DAMAGE_PLAYER | DDBM_TRACE_DAMAGED_PLAYERS);
   if( result ){
      poison(0, BYTE0(result));
      poison(1, BYTE1(result));

       int value = get_card_instance(player, card)->targets[1].player;
      int t_player = BYTE0(value) ? 0 : 1;
      int legacy = create_legacy_effect(player, card, &sabertooth_cobra_legacy);
      get_card_instance(player, legacy)->targets[0].player = t_player;
   }

   return 0;
}
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7436
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 345 times

Re: Card Development - talk about cards code here

Postby drool66 » 02 May 2021, 21:35

Can't it just be a regular upkeep trigger? Nafs Asp triggers on the draw step, which is why it uses EVENT_PHASE_CHANGED. This works for me:
Code: Select all
int sabertooth_cobra_legacy(int player, int card, event_t event){
   upkeep_trigger_ability(player, card, event, player);

   if( event == EVENT_UPKEEP_TRIGGER_ABILITY ){
      poison(player, 1);
      kill_card(player, card, KILL_EXILE);
   }
   if( event == EVENT_RESOLVE_ACTIVATION ){
      card_instance_t* instance = get_card_instance(player, card);
      kill_card(instance->parent_controller, instance->parent_card, KILL_EXILE);
   }

   return generic_activated_ability(player, card, event, 0, MANACOST_X(2), 0, NULL, NULL);
}
int card_sabertooth_cobra(int player, int card, event_t event){
/* CARD_ID_SABERTOOTH_COBRA   2589
Sabertooth Cobra   |2|G
Creature - Snake 2/2
Whenever ~ deals damage to a player, that player gets a poison counter. The player gets another poison counter at the beginning of their next upkeep unless
 they pay |2 before that step. (A player with ten or more poison counters loses the game.) */
int result = damage_dealt_by_me(player, card, event, DDBM_MUST_DAMAGE_PLAYER | DDBM_TRACE_DAMAGED_PLAYERS);
   if( result ){
      poison(0, BYTE0(result));
      poison(1, BYTE1(result));

      int value = get_card_instance(player, card)->targets[1].player;
      int t_player = BYTE0(value) ? 0 : 1;
      if( t_player == player){
         create_legacy_activate(player, card, &sabertooth_cobra_legacy);
      }
      else{
         create_legacy_activate_for_opponent(player, card, &sabertooth_cobra_legacy, player, card);
      }
   }

   return 0;
}
This way the other player can actually pay {2} any time before their next upkeep, but not at their next upkeep. Any idea why Nafs Asp was changed from this? (see functions.c::legacy_effect_activated())
User avatar
drool66
 
Posts: 609
Joined: 25 Nov 2010, 22:38
Has thanked: 148 times
Been thanked: 133 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 03 May 2021, 08:19

I think Gargaroz was trying to make it more compliant with rules and avoid issues with both players activating the card/change control, (not feasible back then) cards that check for activations, cards altering activation costs and there is another issue that other cards using it's effect card they lost 1 life if they had their own effect active also in draw phase ( or this last thing happens with both implementations, I don't remember ).
I think Korath in shandalar somehow suppress the activation so it's not seen by other cards looking for activations.
35fa35c ("[SA] clean up activation triggers", 2018-09-30)
f113c04 ("[SA] +Training Grounds, Heartstone", 2017-02-04)

Also your implementation fails to combine the two poison effects as player gets a poison counter from combat damage instead of damaged player (effect card ok). It's more tricky than it seems and keep in mind that we need to keep the code/function that takes care the "whenever damage" see double strike.

Heartstone didn't see the activation but I guess this is because it doesn't check for effect cards at all.
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7436
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 345 times

Re: Card Development - talk about cards code here

Postby drool66 » 03 May 2021, 17:23

Oh right - I didn't test it completely, but how about this?
Code: Select all
   if( damage_dealt_by_me(player, card, event, DDBM_MUST_DAMAGE_PLAYER | DDBM_TRACE_DAMAGED_PLAYERS) ){
      card_instance_t* instance = get_card_instance(player, card);
      int times_damaged[2] = { BYTE0(instance->targets[1].player), BYTE1(instance->targets[1].player) };
      instance->targets[1].player = 0;
      int p;
      for(p=0; p<2; p++){
         while( times_damaged[p] ){
            poison(p, 1);

            if( player == p ){
               create_legacy_activate(player, card, &sabertooth_cobra_legacy);
            }
            else{
               create_legacy_activate_for_opponent(player, card, &sabertooth_cobra_legacy, player, card);
            }
            times_damaged[p]--;

         }
      }
   }
What you're saying is Heartstone et al. correctly ignores this cost, right? It could affect it if its test included effects as well as creatures.
User avatar
drool66
 
Posts: 609
Joined: 25 Nov 2010, 22:38
Has thanked: 148 times
Been thanked: 133 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 07 May 2021, 13:33

I think there is a conflict of GAA_ONCE_PER_TURN and cards with two or more activated abilities and so GAA_ONCE_PER_TURN doesn't work. I tried several ways and either I got it to work once in a whole game like below implementation (if I remember correctly) or multiple times per turn. The same bug affects also Gaea's Touch here:
viewtopic.php?f=86&t=22840&p=239041&hilit=Gaea%27s+Touch#p239041

Code: Select all
int card_sawback_manticore(int player, int card, event_t event){
   /* CARD_ID_SAWBACK_MANTICORE   2595
   Sawback Manticore   |3|R|G
   Creature - Manticore 2/4
   |4: ~ gains flying until end of turn.
   |1: ~ deals 2 damage to target attacking or blocking creature. Activate this ability only if ~ is attacking or blocking and only once each turn. */

   if( ! IS_GAA_EVENT(event) ){
      return 0;
   }

   target_definition_t td;
   default_target_definition(player, card, &td, TYPE_CREATURE);
   td.required_state = TARGET_STATE_IN_COMBAT;

   card_instance_t *instance = get_card_instance(player, card);

   enum{
      CHOICE_FLYING = 1,
      CHOICE_DAMAGE
   };

   if( event == EVENT_CAN_ACTIVATE ){
      if( generic_activated_ability(player, card, event, 0, MANACOST_X(4), 0, NULL, NULL) ){
         return 1;
      }
      if( is_attacking(player, card) || blocking(player, card, event) ){
         return generic_activated_ability(player, card, event, GAA_CAN_TARGET | GAA_ONCE_PER_TURN, MANACOST_X(1), 0, &td, NULL);
      }
   }

   if( event == EVENT_ACTIVATE ){
      int abils[3] = {
                  0,
                  generic_activated_ability(player, card, EVENT_CAN_ACTIVATE, 0, MANACOST_X(4), 0, NULL, NULL),
                  generic_activated_ability(player, card, EVENT_CAN_ACTIVATE, GAA_CAN_TARGET | GAA_ONCE_PER_TURN, MANACOST_X(1), 0, &td, NULL)
      };
      int priorities[3] = {
                     0,
                     5,
                     current_phase == PHASE_AFTER_BLOCKING ? 10 : 0,
      };
      int choice = DIALOG(player, card, event, DLG_RANDOM,
                     "Gains flying", abils[1], priorities[1],
                     "Deal damage",   abils[2], priorities[2]);
      if( ! choice ){
         spell_fizzled = 1;
         return 0;
      }
      if( choice == CHOICE_FLYING ){
         return generic_activated_ability(player, card, event, 0, MANACOST_X(4), 0, NULL, NULL);
      }
      if( choice == CHOICE_DAMAGE ){
         return generic_activated_ability(player, card, event, GAA_ONCE_PER_TURN | GAA_CAN_TARGET | GAA_LITERAL_PROMPT, MANACOST_X(1), 0, &td,
                                    "Select target attacking or blocking creature.");
      }
   }

   if( event == EVENT_RESOLVE_ACTIVATION ){
      if( instance->info_slot == CHOICE_FLYING ){
         pump_ability_until_eot(player, card, instance->parent_controller, instance->parent_card, 0, 0, KEYWORD_FLYING, 0);
      }
      if( instance->info_slot == CHOICE_DAMAGE ){
         if( valid_target(&td) ){
            damage_target0(player, card, 2);
         }
      }
   }

   return 0;
}
drool66 wrote:What you're saying is Heartstone et al. correctly ignores this cost, right? It could affect it if its test included effects as well as creatures.
I am saying that correctly ignores this cost but for the wrong reason. As it doesn't affect activated abilities that use effect cards. I haven't tested but is it possible to make Heartstone and co to work correctly with effect cards coming only from creatures (or X type, color, etc) and not affect all effect cards?
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7436
Joined: 13 May 2010, 12:17
Has thanked: 636 times
Been thanked: 345 times

Re: Card Development - talk about cards code here

Postby drool66 » 07 May 2021, 16:06

Without testing, I'm pretty sure you just need to clear targets[2].player on cleanup, ie.
Code: Select all
   if( event == EVENT_CLEANUP )
      get_card_instance(player, card)->targets[2].player = 0;
EVENT_CLEANUP is even a GAA_EVENT, so you can put it under there.

is it possible to make Heartstone and co to work correctly with effect cards coming only from creatures (or X type, color, etc) and not affect all effect cards?
Almost definitely could be done in set_cost_mod_for_activated_abilities by checking type == TYPE_EFFECT and checking damage_source_player/card from there, checking of course that it is still in play. Effects like Sabertooth Cobra's that we specifically don't want using cost mods then couldn't use generic_activated_ability(), specifically charge_mana_for_activated_ability() - and just use charge_mana / _multi()
User avatar
drool66
 
Posts: 609
Joined: 25 Nov 2010, 22:38
Has thanked: 148 times
Been thanked: 133 times

Previous

Return to Development

Who is online

Users browsing this forum: No registered users and 7 guests


Who is online

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

Login Form