It is currently 16 Apr 2024, 11:51
   
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, Aswan jaguar, gmzombie, stassy, CCGHQ Admins

Re: Card Development - talk about cards code here

Postby drool66 » 06 Mar 2022, 15:28

From the documentation of check_battlefield_for_special_card():
Code: Select all
   //   mode = 0 -> return 1 if a card of the requested type is found. Otherwise, it'll return 0;
   //   mode = 1 -> return the best choice among the cards of the requested type. If nothing is found, it will return -1;
   //   mode = 2 -> return the worst return AI best choice of that card. If nothing is found, it will return -1;
   //   mode = 4 -> return the global count of cards of the requested type.
   //   mode = 8 -> cards found must be legal targets for (player, card).  Strenuously avoid.
It looks like the enum for these is under "cbfsc_flags_t" So you can probably remove "this_test.qty = result" and check:
Code: Select all
 if( check_battlefield_for_special_card(player, card, instance->targets[0].player, CBFSC_GET_COUNT, &this_test) == result ){...
(and maybe update the documentation to include the enums while you're at it)

Correct me if I'm wrong, but I think
Code: Select all
if( result < 1 ){ result = 0; }
should be
Code: Select all
if( result < 1 ){ result = 1; }
or maybe better yet:
Code: Select all
if( result < 1 ){ spell_fizzled = 1; return 0; }
User avatar
drool66
Programmer
 
Posts: 1163
Joined: 25 Nov 2010, 22:38
Has thanked: 186 times
Been thanked: 267 times

Re: Card Development - talk about cards code here

Postby Korath » 06 Mar 2022, 20:24

The underlying problem is that test_definition_t doesn't really model anything more specific than "a bunch of arguments with defaults, mostly 0 and most of the rest -1". None of the functions accepting one use all of its fields. Some of them use the same fields differently. Most forward it to either of the equally-misnamed new_make_test() or new_make_test_in_play(), which handle just enough fields to give you false confidence in what they do. Neither does anything with qty, though, and neither does check_battlefield_for_special_card().

My advice: loop over the cards directly. It's simpler, less verbose, and you actually know what it's doing.
Code: Select all
card_instance_t* inst;
int p = instance->targets[0].player;
for (int c = 0; c < active_cards_count[p]; ++c)
  if ((inst = in_hand(p, c)) && (inst->color & color_res))
    --result;
if (result == 0)
  draw_cards(player, 1);
Also:
  • IS_AI() is logically a boolean, not 1 or 0. if (IS_AI(player)) and if (!IS_AI(player)), never if (player == HUMAN) and if (IS_AI(player)) (both true for player 0 during speculation), and certainly not if (player == IS_AI(player)), which will be true for player 0 outside of speculation (overwriting your choose_a_number() call), always true for player 1 during a one-player game, and never true for player 1 in multiplayer.
  • recorded_rand() is a poor fit for guessing-game cards. The AI will pick the most advantageous number for it every time. Either do that explicitly (that is, count the number of cards matching the color, and just use that for what the AI picks), or pick a random number at real resolution to make it look like the AI is guessing.
  • You don't ever tell the player what number the AI guessed.
User avatar
Korath
DEVELOPER
 
Posts: 3707
Joined: 02 Jun 2013, 05:57
Has thanked: 496 times
Been thanked: 1106 times

Autumn's Veil

Postby Aswan jaguar » 10 Mar 2022, 14:44

While trying to add non shroud/hexproof cards with clause "can't be the targets of" I came across Autumn's Veil can we currently do it's 1st ability "Spells you control can't be countered by |Sblue or |Sblack spells this turn,"?
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 8078
Joined: 13 May 2010, 12:17
Has thanked: 730 times
Been thanked: 458 times

Re: Card Development - talk about cards code here

Postby drool66 » 10 Mar 2022, 17:53

It's on my "Top Ten Wishlist"
I'm pretty sure we can, but we should also fix "cannot be countered" in general. Currently it's just given "STATE_CANNOT_TARGET" while on the stack, which is wrong. It can be targeted, just not countered. So you could, for example, target it with Essence Capture, get the counter, and then the spell would resolve. I think STATE_CANNOT_TARGET is good for use in spells with Split Second. I've been trying to come up with something for uncounterable that doesn't use another state_t bit, but I think that's what we might have to do. We still have 0x4000000 and 0x8000000, and we can reclaim several if I can ever figure out how to do proper trigger cards.
User avatar
drool66
Programmer
 
Posts: 1163
Joined: 25 Nov 2010, 22:38
Has thanked: 186 times
Been thanked: 267 times

Re: Card Development - talk about cards code here

Postby Korath » 11 Mar 2022, 13:13

drool66 wrote:I think STATE_CANNOT_TARGET is good for use in spells with Split Second.
Better than nothing, maybe, but I don't know that I'd call it "good".
I've been trying to come up with something for uncounterable that doesn't use another state_t bit, but I think that's what we might have to do.
You've got room for however many bits you want now in card_ext_t. Just be sure it's checked in card_instances_should_be_displayed_identically() if you're going to use the icon.
User avatar
Korath
DEVELOPER
 
Posts: 3707
Joined: 02 Jun 2013, 05:57
Has thanked: 496 times
Been thanked: 1106 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 12 Mar 2022, 14:46

I am trying to code Peace Talks it works except the timing. I can't find how to do correctly the duration "This turn and next turn" timing for either ability. Currently effects your turn played as a sorcery and opponents turn.
Ruling:
It affects the current turn and the next turn. This could affect the same player twice if they have two turns in a row, or it could affect different players. In a multiplayer game it affects exactly two turns, not a full round of turns.

Code: Select all
static int peace_talks_effect(int player, int card, event_t event){
   if( in_play(player, card) ){
      give_shroud_to_player(player, card, event);
      give_shroud_to_player(1-player, card, event);
   }
   if( event == EVENT_ABILITIES && is_what(affected_card_controller, affected_card, TYPE_PERMANENT) && affect_me(affected_card_controller, affected_card) )
   special_abilities2(affected_card_controller, affected_card, event, SP_KEYWORD2_GRANTED_PROT_MISC);

   card_instance_t* inst;
   if( event == EVENT_PROT_MISC && (inst = get_card_instance(affected_card_controller, affected_card)) && affect_me(affected_card_controller, affected_card)
      && is_what(affected_card_controller, affected_card, TYPE_PERMANENT)
      && BYTE3(event_result) == IPF_TARGET )
   {
      event_result |= 1;
   }
   // Instead of "This turn and next turn" works only if next turn is opponent's turn so not working correctly if a Time Walk effect has changed that
   if( eot_trigger(player, card, event) && current_turn == 1-player ){
      kill_card(player, card, KILL_EXILE);
   }
   return 0;
}

void nobody_can_attack_this_turn_and_next_turn(int player, int card, int t_player);
int card_peace_talks(int player, int card, event_t event){
   /* CARD_ID_PEACE_TALKS   2755
   Peace Talks   |1|W
   Sorcery
   This turn and next turn, creatures can't attack, and players and permanents can't be the targets of spells or activated abilities. */

   if( event == EVENT_RESOLVE_SPELL ){
      //nobody_can_attack_until_your_next_turn(player, card, ANYBODY);
      nobody_can_attack_this_turn_and_next_turn(player, card, ANYBODY);
      alternate_legacy_text(1, player, create_legacy_effect(player, card, &peace_talks_effect));
      kill_card(player, card, KILL_SACRIFICE);
   }
   return basic_spell(player, card, event);
}
in functions.c
Code: Select all
static int effect_nobody_can_attack_until_eot(int player, int card, event_t event)
......
......
if( event == EVENT_CLEANUP && current_turn == 1-player && (instance->targets[1].card & 16) ){
      end_effect = 1;
   }
....
....
void nobody_can_attack_this_turn_and_next_turn(int player, int card, int t_player)
{
  int leg = create_legacy_effect(player, card, effect_nobody_can_attack_until_eot);

  if (leg != -1)
   {
     alternate_legacy_text(2, player, leg);

     card_instance_t* legacy = get_card_instance(player, leg);
     legacy->targets[1].player = t_player;
     legacy->targets[1].card = 16;
   }
}
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 8078
Joined: 13 May 2010, 12:17
Has thanked: 730 times
Been thanked: 458 times

Re: Card Development - talk about cards code here

Postby Korath » 12 Mar 2022, 16:52

Set a flag on the effect at EVENT_BEGIN_TURN, and kill the card at EVENT_CLEANUP only if the flag is set. (Not eot_trigger(), which is "until the start of (some turn's) end step", and don't check current_turn at all. Not to mention the irritating pause at every turn's end step, and the spurious target availability for Stifles in players' hands.)

Moving forward, you should really, really make duration independent of specific effects. "Until the end of the next turn" actually doesn't show up on any other cards, surprisingly enough, but "until the end of your next turn" and so on do again and again on all sorts of effects. It's a problem you only need to solve once.

In any case, the hard part of this card is making it only affect spells and activated abilities. You have it affecting triggered abilities, too; what you've done is no different than giving the permanents shroud, like you did to the players, except with a different icon.
User avatar
Korath
DEVELOPER
 
Posts: 3707
Joined: 02 Jun 2013, 05:57
Has thanked: 496 times
Been thanked: 1106 times

Re: Reknit can't regenerate enchantments and planeswalkers

Postby Aswan jaguar » 14 Mar 2022, 12:19

Below code for Reknit can't regenerate enchantments and planeswalkers. Regeneration effects don't trigger when a planeswalker or an enchantment gets killed.

Code: Select all
int card_reknit(int player, int card, event_t event){
   /* CARD_ID_REKNIT   9646
   Reknit   |1|GW
   Instant
   Regenerate target permanent. */

   modify_cost_for_hybrid_spells(player, card, event, 0);

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

   target_definition_t td;
   default_target_definition(player, card, &td, TYPE_PERMANENT|TARGET_TYPE_PLANESWALKER);// aswan jaguar-only creature/artifact/lands are working has regeneration in exe to be expanded?
   td.required_state = TARGET_STATE_DESTROYED;
   td.preferred_controller = player;
   if( player == AI ){
      td.special = TARGET_SPECIAL_REGENERATION;
   }

   card_instance_t *instance = get_card_instance(player, card);

   if( event == EVENT_CAN_CAST ){
      return generic_spell(player, card, event, GS_CAN_TARGET | GS_REGENERATION, &td, "TARGET_PERMANENT", 1, NULL);
   }

   if( event == EVENT_CAST_SPELL && affect_me(player, card) ){
      if( hybrid_casting(player, card, 0) ){
         return generic_spell(player, card, event, GS_CAN_TARGET | GS_REGENERATION, &td, "TARGET_PERMANENT", 1, NULL);
      }
      else{
         spell_fizzled = 1;
      }
   }

   if( event == EVENT_RESOLVE_SPELL ){
      if( valid_target(&td) && can_be_regenerated(instance->targets[0].player, instance->targets[0].card) ){
         regenerate_target(instance->targets[0].player, instance->targets[0].card);
      }
      kill_card(player, card, KILL_DESTROY);
   }

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

Re: Card Development - talk about cards code here

Postby Korath » 14 Mar 2022, 20:15

Yes, only artifacts, creatures, and lands can be regenerated in the base game. kill_card(), at least, still enforces that. I see draw_smallcard_normal()'s already been fixed. I seem to recall there's at least one other place that looks at type for regeneration, but I don't remember where, and it's been a very, very long time since I fixed it in Shandalar.

There's also two unreplaced functions with specific hacks to make Death Ward work, and that'll apply just as much to similar instants. They look for the card's original MicroProse function address, so either they aren't really necessary after all, or they're broken and nobody's played the card and noticed the breakage for years.
User avatar
Korath
DEVELOPER
 
Posts: 3707
Joined: 02 Jun 2013, 05:57
Has thanked: 496 times
Been thanked: 1106 times

Re: Card Development - talk about cards code here

Postby Gargaroz » 20 Apr 2022, 14:30

Hi guys, Gargaroz here, hope you're all fine.
After a long period and some quite serious problems, I finally have some free time, so if you want an hand in the development just let me know!
----
- Current / medium term task: adjusting the code for making Misdirection and such usable
- Long term task: inserting all the good stuff I left out from the "Golden Years" mod
Gargaroz
Programmer
 
Posts: 7097
Joined: 06 Nov 2009, 11:11
Has thanked: 82 times
Been thanked: 595 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 20 Apr 2022, 19:33

Hi Gargaroz, good to hear from you. I hope you are fine, too.
Is something you would like to work on?
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 8078
Joined: 13 May 2010, 12:17
Has thanked: 730 times
Been thanked: 458 times

Re: Card Development - talk about cards code here

Postby Gargaroz » 21 Apr 2022, 13:53

Mainly coding new / old missing cards, but I could contribute with overall engine as I previously did.
But first of I need access to the code as everything I had is on my old laptop and righ now I have no way to get it.
----
- Current / medium term task: adjusting the code for making Misdirection and such usable
- Long term task: inserting all the good stuff I left out from the "Golden Years" mod
Gargaroz
Programmer
 
Posts: 7097
Joined: 06 Nov 2009, 11:11
Has thanked: 82 times
Been thanked: 595 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 21 Apr 2022, 14:11

Everything is in server ask Korath for the new address and get updated. If you need any other help with git pm drool66 unfortunately I won't be able to help you.
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 8078
Joined: 13 May 2010, 12:17
Has thanked: 730 times
Been thanked: 458 times

[fixed]Scourge of the Throne

Postby Aswan jaguar » 02 Jan 2023, 18:26

I solved an issue with Scourge of the Throne but it has another one that in the extra combat it still untaps the attacking creatures. It has two effects that trigger in combat and each has different conditions to trigger one for dethrone that should happen at each combat and the other that has to trigger only the the first time each turn. Any idea?
current code:
Code: Select all
int card_scourge_of_the_throne(int player, int card, event_t event){
   /*
     Scourge of the Throne 4RR
     Creature - Dragon
     Flying
     Dethrone (Whenever this creature attacks the player with the most life or tied for most life, put a +1/+1 counter on it.)
     Whenever ~ attacks for the first time each turn, if it's attacking the player with the most life or tied for most life, untap all attacking creatures.
       After this phase, there is an additional combat phase.
     5/5
   */

   dat_ability(player, card, event, RESOLVE_TRIGGER_MANDATORY, 0);

   if ( event == EVENT_DAT_ABILITY && life[1-player] >= life[player] )
   {
      add_1_1_counter(player, card, player, card);
      if( get_card_instance(player, card)->targets[1].player != 66 ){
         test_definition_t this_test;
         default_test_definition(&this_test, TYPE_CREATURE);
         this_test.state = STATE_ATTACKING;
         new_manipulate_all(player, card, player, &this_test, ACT_UNTAP);
         extra_combat_effect(player, card, event, 0, &this_test, 0);
         get_card_instance(player, card)->targets[1].player = 66;
      }
   }

   if( event == EVENT_CLEANUP ){
      get_card_instance(player, card)->targets[1].player = 0;
   }

   return 0;
}
Last edited by Aswan jaguar on 03 Jan 2023, 10:18, edited 1 time in total.
Reason: fixed
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 8078
Joined: 13 May 2010, 12:17
Has thanked: 730 times
Been thanked: 458 times

Re: Card Development - talk about cards code here

Postby drool66 » 02 Jan 2023, 21:52

Something like this?
Code: Select all
int card_scourge_of_the_throne(int player, int card, event_t event){
   /*
     Scourge of the Throne 4RR
     Creature - Dragon
     Flying
     Dethrone (Whenever this creature attacks the player with the most life or tied for most life, put a +1/+1 counter on it.)
     Whenever ~ attacks for the first time each turn, if it's attacking the player with the most life or tied for most life, untap all attacking creatures.
       After this phase, there is an additional combat phase.
     5/5
   */
   // maybe something like, so it doesn't trigger when we don't want it to:
   // if (xtrigger_condition() == XTRIGGER_ATTACKING && affect_me(player, card) && reason_for_trigger_controller == player &&
   // (life[1-current_turn] >= life[current_turn] || get_card_instance(player, card)->targets[1].player != 66))
   dat_ability(player, card, event, RESOLVE_TRIGGER_MANDATORY, 0);

   if ( event == EVENT_DAT_ABILITY )
   {
      if( life[1-current_turn] >= life[current_turn] ) // current_turn is overall more consistent with the attacker functions, but I'd say it's up to you
          add_1_1_counter(player, card, player, card);
      if( get_card_instance(player, card)->targets[1].player != 66 ){
         test_definition_t this_test;
         default_test_definition(&this_test, TYPE_CREATURE);
         this_test.state = STATE_ATTACKING;
         new_manipulate_all(player, card, player, &this_test, ACT_UNTAP);
         extra_combat_effect(player, card, event, 0, &this_test, 0);
         get_card_instance(player, card)->targets[1].player = 66;
      }
   }

   if( event == EVENT_CLEANUP ){
      get_card_instance(player, card)->targets[1].player = 0;
   }

   return 0;
}
This card will be making two separate trigger cards soon enough though.
User avatar
drool66
Programmer
 
Posts: 1163
Joined: 25 Nov 2010, 22:38
Has thanked: 186 times
Been thanked: 267 times

PreviousNext

Return to Development

Who is online

Users browsing this forum: No registered users and 19 guests


Who is online

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

Login Form