Thank you guys. Looking at dialog_impl I had the impression that there is much more than meets the eye, so I didn't even dare to bother with it. Someone (Korath as I know now) spend a lot of time and effort and probably for a very good reason.
And I tried a lot to fix this.
I know what you mean... unfortunately, this kind of bug can't be tackled with code inspection as the code looks (and mostly is) ok. Since this is a nice use case for the logger I outlined below what I did and why so you can try it at home if you are interested (will also add this to the documentation).
It took several iterations. I plugged everything in the code below for ease of explanation.
First, you need to inlcude the header. I do this in manalink.h as it is included everywhere. Mine looks like this:
- Code: Select all
#include "defs.h"
// DEBUG
#include "macrologger.h"
#define LOG_LEVEL DEBUG_LEVEL
#ifdef __cplusplus
extern "C" {
#endif
Looking at
Treva, the Renewer and
Trygon Predator (which has a similar mechanic but works) I thought that the issue is probably within the invasion_dragon function as the rest of
Treva, the Renewer never got called. So I prepared it like this (in several steps):
- Code: Select all
static void invasion_dragon(int player, int card, event_t event, int cless, int black, int blue, int green, int red, int white){
if (damage_dealt_by_me(player, card, event, DDBM_MUST_DAMAGE_PLAYER | DDBM_TRACE_DAMAGED_PLAYERS | DDBM_MUST_BE_COMBAT_DAMAGE))
{
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;
// #1
LOG_DEBUG("Damage dealt by me, player = %d", player);
LOG_DEBUG("times_damaged = {%d, %d}", times_damaged[0], times_damaged[1]);
APNAP(p,{
if( times_damaged[p] ){
char buffer[100];
if( p == player ){
scnprintf(buffer, 100, "Activate %s (you were damaged)", cards_ptr[get_id(player, card)]->name);
}
else{
scnprintf(buffer, 100, "Activate %s (opponent was damaged)", cards_ptr[get_id(player, card)]->name);
// #2
LOG_DEBUG("Opponent was damaged");
}
// #3
LOG_DEBUG("times damaged[p] = %d", times_damaged[p]);
LOG_DEBUG("has_mana_multi(player, cless, black, blue, green, red, white) = %d", has_mana_multi(player, cless, black, blue, green, red, white));
while( times_damaged[p] && has_mana_multi(player, cless, black, blue, green, red, white) ){
// #4
LOG_DEBUG("Entering while");
// #6
LOG_DEBUG("event = %d", event);
int choice = DIALOG(player, card, event, DLG_RANDOM, DLG_NO_STORAGE, DLG_NO_CANCEL,
buffer, 1, p != player ? 10 : 1,
"Decline", 5);
// #5
LOG_DEBUG("choice = %d", choice);
if( choice == 1 ){
charge_mana_multi(player, cless, black, blue, green, red, white);
if( spell_fizzled != 1 ){
SET_BYTE1(instance->info_slot) = p;
call_card_function(player, card, EVENT_DAMAGE_TRIGGER_ABILITY);
instance->info_slot = 0;
}
}
times_damaged[p]--;
// #4
LOG_DEBUG("Leaving while, times_damaged[p] = %d", times_damaged[p]);
}
}
};
);
}
}
After compiling I run the code either through bash (Linux subsystem under Windows) or the cmd prompt. The syntax is
bash: ./Magic.exe 2>Treva.log
cmd: Magic.exe 2>Treva.log
Once I attacked with
Treva, the Renewer I ended the program to fully write the log file and have a look. The function is called several (30 or so) times, so I just look at the last entries. Usually the time stamp of those is a bit (a second or so) later. This went as follows.
#1 logging:
To see whether the function is entered, the player is right and the damage is assigned correctly. My first suspicion was that there is an issue with times_damaged and
Trygon Predator works whereas it shouldn't.
#1 output:
2020-08-14 07:23:00 | DEBUG | invasion.c | invasion_dragon:472 | Damage dealt by me, player = 0
2020-08-14 07:23:00 | DEBUG | invasion.c | invasion_dragon:473 | times_damaged = {0, 1}
#2 logging:
Seems to be ok. Let's see whether this squares with the damage assignment.
#2 output:
2020-08-14 07:23:00 | DEBUG | invasion.c | invasion_dragon:513 | Opponent was damaged
#3 logging:
Works again. So maybe we don't enter the while loop. I don't know what has_mana_multi does precisely, maybe this causes an issue. I log times_damaged for good measure so that I don't miss anything (just because you're paranoid...).
#3 output:
2020-08-14 07:23:00 | DEBUG | invasion.c | invasion_dragon:513 | times damaged[p] = 1
2020-08-14 07:23:00 | DEBUG | invasion.c | invasion_dragon:513 | has_mana_multi(player, cless, black, blue, green, red, white) = 1
#4 logging:
Both 1, so we enter the while loop. If everything works as I think we should be inside only once. Let's check this.
#4 output:
2020-08-14 07:23:00 | DEBUG | invasion.c | invasion_dragon:513 | Entering while
2020-08-14 07:23:00 | DEBUG | invasion.c | invasion_dragon:513 | Leaving while, times_damaged[p] = 0
#5 logging:
As expected. Good. It is likely that choice != 1. Let's see.
#5 output:
2020-08-14 07:23:00 | DEBUG | invasion.c | invasion_dragon:513 | choice = 0
#6 logging:
BINGO! Looking up DIALOG and dialog_impl tells me that only certain events work. No idea what event is used when calling DIALOG but obviously not the right one. Let's see which one it is.
#6 output:
2020-08-14 07:23:00 | DEBUG | invasion.c | invasion_dragon:513 | event = 126
Coda:
126 is 0x7E (I use the windows calculator for the conversion). Looking in defs.h I find the event code in line 230, it is called EVENT_RESOLVE_TRIGGER (which makes intuitive sense).
NB: I didn't use the debugger as I learned in the meanwhile that these functions are called several times. Always continuing the exection while running the risk to miss the proper call is cumbersome and errorprone, so this is not the preferred approach in a case like this.
Edit. I just saw that you could make your life easier during the last step with something like this, printing the event id in hex:
- Code: Select all
LOG_DEBUG("event = %04x", event);