Card Development - talk about cards code here
Discuss Upcoming Releases, Coding New Cards, Etc.
PLEASE DO NOT REPORT BUGS HERE!
PLEASE DO NOT REPORT BUGS HERE!
Moderators: BAgate, drool66, Aswan jaguar, gmzombie, stassy, CCGHQ Admins
Card Development - talk about cards code here
by Aswan jaguar » 08 Aug 2020, 15:45
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.
- 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
Reason: strikethrough fixed
---
Trying to squash some bugs and playtesting.
Trying to squash some bugs and playtesting.
-
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
by 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.
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
Argivian Archaeologist in the Library of Leng studying the Spells of the Ancients
Re: Card Development - talk about cards code here
by 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.
Trying to squash some bugs and playtesting.
-
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
by 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
Argivian Archaeologist in the Library of Leng studying the Spells of the Ancients
Breath of Dreams - DONE
by Aswan jaguar » 06 Oct 2020, 17:52
- 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
Reason: strikethrough fixed
---
Trying to squash some bugs and playtesting.
Trying to squash some bugs and playtesting.
-
Aswan jaguar - Super Tester Elite
- Posts: 8078
- Joined: 13 May 2010, 12:17
- Has thanked: 730 times
- Been thanked: 458 times
Balduvian Shaman - DONE
by 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
Reason: done
---
Trying to squash some bugs and playtesting.
Trying to squash some bugs and playtesting.
-
Aswan jaguar - Super Tester Elite
- Posts: 8078
- Joined: 13 May 2010, 12:17
- Has thanked: 730 times
- Been thanked: 458 times
Exploding Borders - DONE
by Aswan jaguar » 18 Oct 2020, 13:21
Can we force recalculation of lands in play after domain ability and before calculating basic land types for damage?
- 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
Reason: strikethrough fixed
---
Trying to squash some bugs and playtesting.
Trying to squash some bugs and playtesting.
-
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
by drool66 » 30 Oct 2020, 07:19
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.For Balduvian Shaman I have no idea how to make it check "that doesn't have cumulative upkeep".
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_CARDSI can't find a way to make it count the land that it put onto the battlefield with the first ability
The latest images for Manalink will be here.
The latest Manalink installation directory will be here. Well, not quite, anymore. Check the latest patches.
The latest Manalink installation directory will be here. Well, not quite, anymore. Check the latest patches.
-
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
by drool66 » 30 Oct 2020, 07:35
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):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.
- 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()
}
The latest images for Manalink will be here.
The latest Manalink installation directory will be here. Well, not quite, anymore. Check the latest patches.
The latest Manalink installation directory will be here. Well, not quite, anymore. Check the latest patches.
-
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
by 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.
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
Reason: add edit
---
Trying to squash some bugs and playtesting.
Trying to squash some bugs and playtesting.
-
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
by drool66 » 31 Oct 2020, 17:03
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 Exploding Borders I went with recalculate_all_cards_in_play()
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.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.
The latest images for Manalink will be here.
The latest Manalink installation directory will be here. Well, not quite, anymore. Check the latest patches.
The latest Manalink installation directory will be here. Well, not quite, anymore. Check the latest patches.
-
drool66 - Programmer
- Posts: 1163
- Joined: 25 Nov 2010, 22:38
- Has thanked: 186 times
- Been thanked: 267 times
Chaos Moon - DONE
by Aswan jaguar » 08 Nov 2020, 14:02
And I don't have the slightest idea what causes this.
- 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);
}
Last edited by Aswan jaguar on 12 Nov 2020, 15:28, edited 2 times in total.
Reason: strikethrough fixed
Reason: strikethrough fixed
---
Trying to squash some bugs and playtesting.
Trying to squash some bugs and playtesting.
-
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
by 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?
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?
The latest images for Manalink will be here.
The latest Manalink installation directory will be here. Well, not quite, anymore. Check the latest patches.
The latest Manalink installation directory will be here. Well, not quite, anymore. Check the latest patches.
-
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
by FastEddie » 09 Nov 2020, 09:54
Nice one!
Just one comment from my side. The scanf here
Just one comment from my side. The scanf here
- Code: Select all
int n = count_permanents_by_type(ANYBODY, TYPE_PERMANENT);
scanf("%d", &n);
---
Argivian Archaeologist in the Library of Leng studying the Spells of the Ancients
Argivian Archaeologist in the Library of Leng studying the Spells of the Ancients
Re: Card Development - talk about cards code here
by Aswan jaguar » 09 Nov 2020, 16:22
It was
Yes, it was a packaging issue. And removed the scanf("%d", &n); code. Thanks, guys.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?
---
Trying to squash some bugs and playtesting.
Trying to squash some bugs and playtesting.
-
Aswan jaguar - Super Tester Elite
- Posts: 8078
- Joined: 13 May 2010, 12:17
- Has thanked: 730 times
- Been thanked: 458 times
Who is online
Users browsing this forum: No registered users and 17 guests