It is currently 16 Apr 2024, 05:00
   
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 Aswan jaguar » 24 Jun 2021, 07:32

Hipparion seems to have issue only with Blaze of Glory which is quite fine by me, I will submit the code. Thanks for the help drool66.
---
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 Aswan jaguar » 06 Jul 2021, 14:06

drool66 wrote:From untap_phasing(), it looks like EVENT_PHASING won't be sent to Teferi's Curse because "player" is the player controlling it, and EVENT_PHASING is only sent to the cards of the player whose turn it is - so if played on another player's creature, it won't see Teferi's Curse at all. I'm pretty sure you'd have to special-case these cards into mirage.c::untap_phasing() and maybe even engine.c::untap_phase(). See how Shimmer is handled for reference - these would probably be less complicated than that card.
I tried this but I can't do it, if you find some time please do it.
---
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 » 09 Jul 2021, 23:53

I have a working concept for Kaervek's Torch. What it does is modify the cost of interrupts cast when the Torch is (card_on_stack_controller, card_on_stack), and indeed, any interrupts I can throw at it cost {2} more. Question is can a card be targeted on the stack in Manalink when these conditions are not true?

Code is as follows:
Code: Select all
int card_kaerveks_torch(int player, int card, event_t event){
/* CARD_ID_KAERVEKS_TORCH   2522 //remain impossible???
Kaervek's Torch   |X|R
Sorcery
As long as ~ is on the stack, spells that target it cost |2 more to cast.
~ deals X damage to any target. */
   if( ! IS_GS_EVENT(player, card, event) ){
      return 0;
   }

   target_definition_t td;
   default_target_definition(player, card, &td, TYPE_CREATURE|TARGET_TYPE_PLANESWALKER);
   td.zone = TARGET_ZONE_CREATURE_OR_PLAYER;

   card_instance_t* instance = get_card_instance(player, card);

   if( event == EVENT_CAST_SPELL && affect_me(player, card) ){
      int card_added = generate_reserved_token_by_id(player, CARD_ID_SPECIAL_EFFECT);
      card_instance_t *inst = get_card_instance(player, card_added);
      inst->targets[0].card = SE_MOD_SPELLS_TARGETING_ME;
      inst->targets[1].player = player;
      inst->targets[1].card = card;
      inst->targets[2].card = 2;
      put_into_play(player, card_added);
   }

   if( event == EVENT_RESOLVE_SPELL ){
      if( valid_target(&td) ){
         damage_target0(player, card, instance->info_slot);
      }
      kill_card(player, card, KILL_DESTROY);
   }

   return generic_spell(player, card, event, GS_CAN_TARGET | GS_X_SPELL, &td, "TARGET_ANY", 1, NULL);
}
and in card_special_effect():
Code: Select all
      if( event == EVENT_MODIFY_COST_GLOBAL ){

...

         if( flags & SE_MOD_SPELLS_TARGETING_ME ){
            if(card_on_stack_controller == instance->targets[1].player && card_on_stack == instance->targets[1].card && is_what(affected_card_controller, affected_card, TYPE_INTERRUPT))
               COST_COLORLESS += instance->targets[2].card;
         }
      }
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 Jul 2021, 23:15

Do the Manalink versions of Brutal Expulsion or Unsubstantiate or similar effects that bounce spells on the stack only let you target the most recently-cast one?

In any case, this'll tie your hands when you eventually let bog-standard Counterspell and Fork effects target any spell on the stack instead of the top one.

This also looks like it'll change the casting cost of any interrupt while this is on top of the stack, even if it doesn't target anything. Manalink doesn't yet have Counterflux (in overload mode), Summary Dismissal, Swift Silence, or Whirlwind Denial, but I'd expect they'd all be implemented as interrupts.
User avatar
Korath
DEVELOPER
 
Posts: 3707
Joined: 02 Jun 2013, 05:57
Has thanked: 496 times
Been thanked: 1106 times

Brood of Cockroaches is fine see edit

Postby Aswan jaguar » 18 Aug 2021, 08:53

I tried a lot but can't make Brood of Cockroaches effect to work when Brood of Cockroaches is controlled by the opponent, it triggers with no effect at all if it triggers at opponent's turn and make owner lose life if it is it's owners turn but doesn't return to hand.
My current code:
Code: Select all
static int brood_of_cockroaches_legacy(int player, int card, event_t event){
   if( eot_trigger(player, card, event) ){
      //card_instance_t *instance = get_card_instance(player, card);
      int owner, position;
      if( find_in_owners_graveyard(player, card, &owner, &position) ){
         lose_life(player, 1);
         int iid = get_grave(owner)[position];
         remove_card_from_grave(owner, position);
         add_card_to_hand(owner, iid);
      }
      kill_card(player, card, KILL_EXILE);
   }
   return 0;
}

int card_brood_of_cockroaches(int player, int card, event_t event){
   /* CARD_ID_BROOD_OF_COCKROACHES   2678
   Brood of Cockroaches   |1|B
   Creature - Insect 1/1
   When ~ is put into your graveyard from the battlefield, at the beginning of the next end step, you lose 1 life and return ~ to your hand. */

   if( this_dies_trigger_for_owner(player, card, event, RESOLVE_TRIGGER_MANDATORY) ){
      create_legacy_effect(player, card, &brood_of_cockroaches_legacy);
   }

   return 0;
}
EDIT: I was wrong this shouldn't trigger if it dies while opponent has stolen it, so this was fine, after all.
Last edited by Aswan jaguar on 02 Sep 2021, 14:54, edited 1 time in total.
Reason: add edit
---
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 » 20 Oct 2021, 16:59

I'm finally getting close to finishing my changes to auras. Before I commit & push, does anyone know what this code does? Right now it's in generic_aura_impl()
Code: Select all
   if( event == EVENT_LEAVES_PLAY_ABILITY &&
         (instance->targets[0].card != -1 ||
         (instance->damage_target_player != instance->targets[2].player && instance->damage_target_card != instance->targets[2].card))
     ){
      instance->targets[2].player = instance->damage_target_player;
      instance->targets[2].card = instance->damage_target_card;
   }
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 » 20 Oct 2021, 19:13

It's the metastasized remnants of an attempt to detect the aura having been attached to a different object, and disable all the things on the old object like removing abilities or subtypes or vigilance (!) that get permanently added or removed from cards as the game state changes instead of being continuously recalculated.

It might make more sense in a less-mangled form. git show 5363b395f:src/functions/functions.c starting at line 8015.
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 drool66 » 21 Oct 2021, 00:10

Oh wow. Do you happen to know of anything else in the larger scope that would examine targets[2] in that context? I can't find anything.
On the bright side it looks like everything there recalcs continuously now that I've got subtypes to do so.
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 » 21 Oct 2021, 00:19

I haven't looked. It was first kept in the non-disably version of the function because there were auras around that were breaking because they looked for their attachment in targets[2] instead of damage_target_player/damage_target_card (or even targets[0], as was mostly the practice then). The similar case for equipment looking in targets[8] is still widespread and easily-greppable.
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 » 02 Nov 2021, 16:06

Korath, do you know why when I check for deathtouch ability in EVENT_CHECK_ABILITIES AI takes it into account only when considering to block but not when attacking? AI doesn't do that with creatures that have deathtouch continually.
e.g for Narnam Cobra:
Code: Select all
   if( event == EVENT_CHECK_ABILITIES && affect_me(player, card) && generic_activated_ability(player, card, EVENT_CAN_ACTIVATE, 0, MANACOST_G(1), 0, NULL, NULL) ){
      check_abilities_keywords |= SP_KEYWORD_DEATHTOUCH;
   }
---
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 » 03 Nov 2021, 02:20

You're not telling the AI that {player,card} can get deathtouch. You're saying it can get regeneration. check_abilities_keywords is a keyword_t.

It won't help you for other sp_keyword_t's, but check_destroys_if_blocked() (evolved from the original ai for Thicket Basilisk, Cockatrice, Infernal Medusa, Abomination, and Battering Ram) is what lets the combat AI know that deathtouch kills cards dead despite insufficient power. get_abilities(...EVENT_ABILITIES...) sets DIFB_DESTROYS_UNPROTECTED to tell it deathtouch is present.
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 drool66 » 26 Nov 2021, 23:39

I'm having a lot of trouble taking can_autotap() out of exe. I cannot get any autotapping behavior to change no matter what I do, including having can_autotap() return 0 unconditionally. What I've done is added:
Code: Select all
manalink.h::
#define QUERYING(val)   (event == EVENT_QUERY && attacking_card == (val))

produce_mana.c::
int plain_mana_producer(int player, int card, event_t event){
   if( QUERYING(QUERY_IS_PLAIN_MANA_PRODUCER) )
      return 1;

   return mana_producer(player, card, event);
}
and the same with plain_mana_producer_tapped(), and mapped them in ManalinkEh.asm; I assigned those addresses to any land or artifact that makes only one mana whose color does not change while it's in play and has no other abilities, or otherwise had the card return one of those functions instead of mana_producer / _tapped().
can_autotap() is declared in manalink.h and looks like this:
Code: Select all
int can_autotap(int player, int card, autotap_t autotap_flags){
   // 0x42fda0
  card_instance_t* inst = in_play(player, card);
  if( !inst )
   return 0;

  iid_t iid = inst->internal_card_id;
  color_test_t colors = (inst->card_color & (COLOR_TEST_ANY | COLOR_TEST_ARTIFACT));

  if( is_what(player, card, TYPE_LAND) )
   colors |= mana_colors_added[player];

  if ( !(colors & COLOR_TEST_ANY)
   || !((cards_data[iid].extra_ability & EA_MANA_SOURCE) || (is_what(player, card, TYPE_LAND) && (mana_colors_added[player] & COLOR_TEST_ANY)))
   || cards_data[iid].extra_ability & EA_PAID_MANASOURCE
   || !can_produce_mana(player, card) )
   return 0;

  if ( (autotap_flags & AUTOTAP_NO_BASIC_LANDS) && (is_basic_land(player, card) || query(player, card, -1, QUERY_IS_PLAIN_MANA_PRODUCER)) )
   return 0;

  if ( (autotap_flags & AUTOTAP_NO_NONBASIC_LANDS) && is_what(player, card, TYPE_LAND) && !is_basic_land(player, card) && !query(player, card, -1, QUERY_IS_PLAIN_MANA_PRODUCER) )
   return 0;

  if ( (autotap_flags & AUTOTAP_NO_DONT_AUTO_TAP) && (inst->state & STATE_NO_AUTO_TAPPING) )
   return 0;

  if ( (autotap_flags & AUTOTAP_NO_ARTIFACTS) && (is_what(player, card, TYPE_ARTIFACT) && !query(player, card, -1, QUERY_IS_PLAIN_MANA_PRODUCER)) )
   return 0;

  if ( (autotap_flags & AUTOTAP_NO_CREATURES) && (is_what(player, card, TYPE_CREATURE) && !query(player, card, -1, QUERY_IS_PLAIN_MANA_PRODUCER)) )
   return 0;

  return 1;
}
query() is already defined in functions.c

patch_move_into_c.pl has added to it:
Code: Select all
###############
# can_autotap #
###############
#42fda0:   55         push   ebp
#42fda1:   8b ec         mov   ebp,esp
#42fda3:   83 ec 0c         sub   esp,0c
jmp_to(0x42fda0 => 0x2007884);
and 0x2007884 is mapped to _can_autotap in ManalinkEh.asm (reclaimed from the guildgates, which are now mapped to plain_mana_producer_tapped()) I copied magic.exe to /src/patches, ran "perl patch_move_into_c.pl" and then moved magic.exe (which had a new filestamp) from /src/patches to /magic_updater and built as normal.
I did run into a problem in /src/patches/Manalink/patch.pm at line 100 - I changed "$filename's" to "$filename s"
Human autotapping still taps things like Academy Ruins at the same priority of something like Plateau. Did I miss something?
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 » 26 Nov 2021, 23:53

Are you executing "make data" in between patching and running? If so, it'll restore magic.exe from HEAD. I generally make an extra commit immediately after running the perl patch to deal with that, then squash before pushing.

Try checking that the Magic.exe in the directory you're running the game from to be sure your changes are sticking.
Code: Select all
objdump -M intel -d Magic.exe --start-address=0x42fda0 --stop-address=0x42fdff
should start with a jmp if so, push, mov, sub if not. In this particular case, you can also try sprinkling a few printf()s in your can_autotap().

It's also a good practice to do a full disassembly before and after patching (the same objdump arguments as above, except omit --start-address and --stop-address), then diff the results, to be sure you didn't change anything else. In particular, I pushed a commit a few hours ago that patches out the last usage of mana_to_untap[] and mana_upkeep[], and things will break very badly if that patch is accidentally undone and we start reusing that data.


Edit after a reread:
drool66 wrote:and then moved magic.exe (which had a new filestamp) from /src/patches to /magic_updater and built as normal
This will definitely be a problem if you're using make data, or the Makefile in magic_updater/ at all, to build data files - the first thing it does is "cp ../Magic.exe .".
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 drool66 » 27 Nov 2021, 01:57

I had no idea, I'm glad I asked. Thank you, everything is working. I also forgot about doing the diff, so thanks also for the reminder.
User avatar
drool66
Programmer
 
Posts: 1163
Joined: 25 Nov 2010, 22:38
Has thanked: 186 times
Been thanked: 267 times

[DONE]Scrying Glass

Postby Aswan jaguar » 06 Mar 2022, 13:43

I am trying to do this very bad card Scrying Glass and after several tries I can't find how to make the chosen number be taken into account only the chosen color is taken into account when the activated ability resolves.

Code: Select all
int card_scrying_glass(int player, int card, event_t event){ // remain
   /* CARD_ID_SCRYING_GLASS   4189
   Scrying Glass   |2
   Artifact
   |3, |T: Choose a number greater than 0 and a color. Target opponent reveals their hand.
    If that opponent reveals exactly the chosen number of cards of the chosen color, you draw a card. */

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

   target_definition_t td;
   default_target_definition(player, card, &td, 0);
   td.zone = TARGET_ZONE_PLAYERS;

   card_instance_t *instance = get_card_instance( player, card );

   if( event == EVENT_RESOLVE_ACTIVATION ){
      if( valid_target(&td) ){
         int result = 0;
         if( player == HUMAN ){
            while( result == 0 ){
                  result = choose_a_number(player, "Choose a number greater than 0.", hand_count[1-player]);
                  if( result < 1 ){
                     result = 0;
                  }
            }
         }

         int color_res = 1<<choose_a_color(player, get_deck_color(player, 1-player));
         if( player == IS_AI(player) ){
            result = recorded_rand(player, hand_count[1-player]) + 1;
         }
         reveal_target_player_hand(instance->targets[0].player);

         test_definition_t this_test;
         default_test_definition(&this_test, TYPE_ANY);
         this_test.color = color_res;
         this_test.qty = result;
         this_test.zone = TARGET_ZONE_HAND;
         if( check_battlefield_for_special_card(player, card, player, 0, &this_test) ){
            draw_cards(player, 1);
         }
      }
   }

   return generic_activated_ability(player, card, event, GAA_UNTAPPED | GAA_CAN_ONLY_TARGET_OPPONENT, MANACOST_X(3), 0, &td, NULL);
}
Last edited by Aswan jaguar on 10 Mar 2022, 14:04, edited 1 time in total.
Reason: retitle
---
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

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