Page 2 of 2

Re: [confirmed]Suspend prevents planewalker abilities

PostPosted: 17 Jan 2022, 00:24
by drool66
Thanks again, Korath; yes, that was the only crash.
I've got the two new functions mostly working; the only issue is something in the AI threading, specifically around the section for determining x-values. I can't get AI to cast an x spell under any circumstances. I bet it's a similar issue around a pointer - I'm not sure if I should do the recasts to char or not. Maybe you can see something obvious I'm overlooking? Maybe a lot of things I'm misunderstanding?
If not, we can just keep your shim and be done with it, although it would be very nice to start changing this function to handle hybrid.
Code: Select all
#define mana_cost_xbugrw   EXE_BYTE_PTR(0x55cf18)
static void fn_402593(card_data_t* cd)
{
  int i;
  for(i=COLOR_COLORLESS;i<=COLOR_WHITE;i++)
   pay_mana_xbugrwaU[i] = mana_cost_xbugrw[i];

  if ( cd->type & TYPE_ARTIFACT )
  {
    PAY_MANA_ARTIFACT = PAY_MANA_COLORLESS;
    PAY_MANA_COLORLESS = 0;
  }
}
#undef mana_cost_xbugrw

#define TENTATIVE_ai_branch_value_99_is_abort            EXE_DWORD(0x7a2fe4)
#define TENTATIVE_mana_paid_for_spell                  EXE_BYTE_PTR(0x55cef0)
#define TENTATIVE_ai_save_values_on_speculative_branch      EXE_FN(int, 0x498F20, int)
#define TENTATIVE_ai_retrieve_values_on_chosen_branch      EXE_FN(int, 0x499050, void)
#define charge_mana_w_global_cost_mod                  EXE_FN(int, 0x4302c0, int, int, color_t, int)
#define TENTATIVE_update_mana_spent                     EXE_FN(int, 0x430260, int)
#define FN_42DE60                                 EXE_FN(int, 0x42DE60, int, int, int, int, int)
static int charge_spell_cost(card_data_t* cd, int player, int card)
{   // 0x402680
   fn_402593(cd);
   card_ptr_t* cp = cards_ptr[ get_id(player, card) ];
   int i, is_x_spell = cp->req_colorless == 40;
   int8_t* byte_55CF20 = (int8_t*)EXE_BYTE_PTR(0x55cf20);   // Amount of surplus free mana?
   int8_t* byte_4EF4FC = (int8_t*)EXE_BYTE_PTR(0x4EF4FC);
   if ( (player == HUMAN || trace_mode & 2) && ai_is_speculating != 1 ){
   //   if ( !v5 )   // something in edx?
   //      return charge_mana_w_global_cost_mod(player, card, COLOR_COLORLESS, 0);

      int result;
      if ( !(result = charge_mana_w_global_cost_mod(player, card, COLOR_COLORLESS, 0)) )
         return 0;

      memcpy(TENTATIVE_mana_paid_for_spell, &mana_paid, 0x1Cu);
      int v7[8];
      for(i=0;i<8;i++)
         v7[i] = byte_55CF20[i];
      if ( *byte_55CF20 < 0 ){
         for(i=0;i<8;i++){
            mana_pool[player][i] -= byte_55CF20[i];
            byte_4EF4FC[player] -= v7[i];
         }
      }
      if( is_x_spell )
         charge_mana(player, 0, -1);
      goto LABEL_33;
   }
   if ( cd->type & TYPE_ARTIFACT ){
      int v9 = PAY_MANA_ARTIFACT;
      PAY_MANA_ARTIFACT = 0;
      return charge_mana_w_global_cost_mod(player, card, COLOR_ARTIFACT, v9);
   }
   int result = charge_mana_w_global_cost_mod(player, card, COLOR_COLORLESS, 0);
   if ( result ){
      result = cd->cc[1];
      if ( result < 0 ){
         memcpy(TENTATIVE_mana_paid_for_spell, &mana_paid, 0x1Cu);
         int v11 = mana_paid[COLOR_ANY];
         int v10 = TENTATIVE_mana_paid_for_spell[COLOR_ANY];
         int v12[8];
         for(i=0;i<8;i++)
            v12[i] = byte_55CF20[i];

         if ( *byte_55CF20 < 0 ){
            for(i=0;i<8;i++){
               mana_pool[player][i] -= byte_55CF20[i];
               byte_4EF4FC[player] -= v12[i];
            }
         }
         if ( player == HUMAN ){
            int old_max_x = max_x_value;
            max_x_value = has_mana(player, COLOR_ANY, 1);
            FN_42DE60(v10, v11, player, 0, -1);
            max_x_value = old_max_x;
         }
         else{
            int amt_for_x;
            if ( ai_is_speculating == 1 ){
               if ( internal_rand(2) ){
                  amt_for_x = has_mana(player, COLOR_ANY, 1);
                  TENTATIVE_ai_branch_value_99_is_abort = amt_for_x;
                  if ( life[HUMAN] < amt_for_x && !internal_rand(3) ){
                     amt_for_x = life[HUMAN];
                     TENTATIVE_ai_branch_value_99_is_abort = life[HUMAN];
                  }
                  if ( max_x_value != -1 && max_x_value < TENTATIVE_ai_branch_value_99_is_abort ){
                     amt_for_x = max_x_value;
                     TENTATIVE_ai_branch_value_99_is_abort = max_x_value;
                  }
               }
               else{
                  int v14 = has_mana(player, COLOR_ANY, 1);
                  if ( v14 )
                     v14 = internal_rand(v14) + 1;
                  amt_for_x = v14;
                  TENTATIVE_ai_branch_value_99_is_abort = v14;
               }
               TENTATIVE_ai_save_values_on_speculative_branch(v11);
            }
            else{
               TENTATIVE_ai_retrieve_values_on_chosen_branch();
               amt_for_x = TENTATIVE_ai_branch_value_99_is_abort;
            }
            int store_max_x_value = max_x_value;
            max_x_value = amt_for_x;
            FN_42DE60(v10, v11, player, 0, -1);
            max_x_value = store_max_x_value;
         }
         LABEL_33:
         result = *byte_55CF20;
         if ( byte_55CF20[COLOR_ANY] != 0 ){
            int found = 0;
            for(i=0;i<8;i++){
               if( byte_55CF20[i] + (mana_pool[player][i] < 0) )
                  found = 1;
               mana_pool[player][i] += byte_55CF20[i];
            }
            if ( found ){
               int8_t v19 = 0;
               for(i=0;i<8;i++)
                  v19 |= ((mana_pool[player][i] > 0) << i);
               EXE_FN(int, 0x4301a0, int, int)(0, v19);
               result = v19;
               memset(mana_pool[player], 0, sizeof(mana_pool[player]));
            }
            else{
               byte_4EF4FC[player] += result;
            }
         }
         if ( !x_value )
            ai_modifier -= 100;
         if ( spell_fizzled == 1 ){
            TENTATIVE_update_mana_spent(*TENTATIVE_mana_paid_for_spell);
            for(i=0;i<8;i++){
               result = TENTATIVE_mana_paid_for_spell[i];
               mana_pool[player][i] += result;
               byte_4EF4FC[player] += result;
            }
         }
         return result;
      }
   }
   return result;
}
#undef TENTATIVE_ai_branch_value_99_is_abort
#undef TENTATIVE_mana_paid_for_spell
#undef TENTATIVE_ai_save_values_on_speculative_branch
#undef TENTATIVE_ai_retrieve_values_on_chosen_branch
#undef charge_mana_w_global_cost_mod
#undef TENTATIVE_update_mana_spent
#undef FN_42DE60
Here's the pseudocode, for reference:
Code: Select all
int __usercall sub_402680@<eax>(int a1@<edi>, int a2, int a3)
{
  int v3; // edx
  int result; // eax
  int v5; // eax
  int v6; // ecx
  int v7; // eax
  int v8; // eax
  int v9; // ecx
  int v10; // eax
  int v11; // ST0C_4
  int v12; // ST0C_4
  int v13; // ecx
  bool v14; // sf
  int v15; // ST0C_4
  int v16; // ecx
  unsigned int v17; // edx
  int v18; // [esp+8h] [ebp-4h]

  sub_402593(a1);
  if ( (!a2 || dword_790640 & 2) && dword_728574 != 1 )
  {
    if ( !v3 )
      return sub_4302C0(a2, a3, 0, 0);
    result = sub_4302C0(a2, a3, 0, 0);
    if ( !result )
      return result;
    qmemcpy(dword_55CEF0, &dword_55CED0, 0x1Cu);
    v5 = (char)dword_55CF20;
    if ( (dword_55CF20 & 0x80u) != 0 )
    {
      v6 = 8 * a2;
      dword_4EF4E0[v6] -= (char)dword_55CF20;
      dword_4EF4FC[v6] -= v5;
    }
    sub_42DE60(a2, 0, -1);
    goto LABEL_33;
  }
  if ( *(_BYTE *)(a1 + 40) & 0x40 )
  {
    v7 = dword_4EF418;
    dword_4EF418 = 0;
    return sub_4302C0(a2, a3, 6, v7);
  }
  result = sub_4302C0(a2, a3, 0, 0);
  if ( result )
  {
    result = *(char *)(a1 + 44);
    if ( result < 0 )
    {
      qmemcpy(dword_55CEF0, &dword_55CED0, 0x1Cu);
      v8 = (char)dword_55CF20;
      if ( (dword_55CF20 & 0x80u) != 0 )
      {
        v9 = 8 * a2;
        dword_4EF4E0[v9] -= (char)dword_55CF20;
        dword_4EF4FC[v9] -= v8;
      }
      if ( a2 == dword_736AD8 )
      {
        v12 = dword_4EF198;
        dword_4EF198 = sub_49D510(a2, 7, 1);
        sub_42DE60(a2, 0, -1);
        dword_4EF198 = v12;
      }
      else
      {
        if ( dword_728574 == 1 )
        {
          if ( sub_44E050(2) )
          {
            v18 = sub_49D510(a2, 7, 1);
            dword_7A2FE4 = v18;
            if ( dword_4EF528 < v18 && !sub_44E050(3) )
            {
              v18 = dword_4EF528;
              dword_7A2FE4 = dword_4EF528;
            }
            if ( dword_4EF198 != -1 && dword_4EF198 < dword_7A2FE4 )
            {
              v18 = dword_4EF198;
              dword_7A2FE4 = dword_4EF198;
            }
          }
          else
          {
            v10 = sub_49D510(a2, 7, 1);
            if ( v10 )
              v10 = sub_44E050(v10) + 1;
            v18 = v10;
            dword_7A2FE4 = v10;
          }
          sub_498F20();
        }
        else
        {
          sub_499050();
          v18 = dword_7A2FE4;
        }
        v11 = dword_4EF198;
        dword_4EF198 = v18;
        sub_42DE60(a2, 0, -1);
        dword_4EF198 = v11;
      }
LABEL_33:
      v13 = 8 * a2;
      result = (char)dword_55CF20;
      if ( (dword_55CF20 & 0x80u) != 0 )
      {
        v14 = (char)dword_55CF20 + dword_4EF4E0[v13] < 0;
        dword_4EF4E0[v13] += (char)dword_55CF20;
        if ( v14 )
        {
          v15 = dword_4EF4E0[v13];
          sub_4301A0(0, v15);
          result = v15;
          dword_4EF4E0[8 * a2] = 0;
        }
        else
        {
          dword_4EF4FC[v13] += result;
        }
      }
      if ( !dword_4EF1A0 )
        dword_7A31A8 -= 100;
      if ( dword_4EF194 == 1 )
      {
        sub_430260(dword_55CEF0);
        v17 = 0;
        do
        {
          result = dword_55CEF0[v17];
          *(int *)((char *)&dword_4EF4E0[v17] + v16) += result;
          *(int *)((char *)dword_4EF4FC + v16) += result;
          ++v17;
        }
        while ( v17 < 8 );
      }
      return result;
    }
  }
  return result;
}

Re: [confirmed]Suspend prevents planewalker abilities

PostPosted: 17 Jan 2022, 05:33
by Korath
First thing I see is that the exe version of fn_402593() returns a value in edx, equal to MIN(0, cd->cc[1]) (so zero for non-x spells, negative for x spells). That's not causing your problems, though, since it only gets used in the human codepath.

Second is that what you're calling FN_42DE60() is the original address of charge_mana(). It takes just the last three arguments you're giving it.

Re: [confirmed]Suspend prevents planewalker abilities

PostPosted: 17 Jan 2022, 18:00
by drool66
[...]equal to MIN(0, cd->cc[1]) (so zero for non-x spells, negative for x spells).
Well, that was the inspiration I needed because it made me remember something. Looking at this line, the gate for the AI speculation on x-values:
Code: Select all
      result = cd->cc[1];
      if ( result < 0 ){
If instead I do a direct comparison to cc[1]:
Code: Select all
      if ( cd->cc[1] < 0 ){
I get a build "error: comparison is always false due to limited range of data type"
So checking instead:
Code: Select all
      if ( is_x_spell ){
( is_x_spell == (cp->req_colorless == 40) )
and viola, AI will cast x spells and everything seems to work great. That seems copacetic to me, what do you think?

EDIT:
Oh right, it's because cc[ ] is a uint. Casting cc[1] to an int8_t and comparing to 0 works better than checking cp->req_colorless. I'll do that, clean up a little and push.

Re: [fixed]Suspend prevents planewalker abilities

PostPosted: 20 Jan 2022, 19:06
by drool66
Magic.exe patch attempted in b0e4359, successful in 4ae8eab
Bug fixed in 7f0680e

Re: [confirmed]Suspend prevents planewalker abilities

PostPosted: 21 Jan 2022, 14:45
by Aswan jaguar
Korath wrote:I see that the version in engine.c has already been fiddled with, despite it being disabled (for instance, by setting STATE_SICKNESS_UNUSED, which still needs to be renamed). It needs to be checked carefully against current assembly first, since it's missing at least one patch, patch_make_manasource_interrupts_interruptible.pl.
drool66 did you also made the check mentioned above against current assembly to see if any other patches are missing and set patch_make_manasource_interrupts_interruptible.pl?

Re: [fixed]Suspend prevents planewalker abilities

PostPosted: 21 Jan 2022, 17:11
by drool66
Yes; I checked everything from the current assembly. patch_make_manasource_interrupts_interruptible.pl was the only thing that had changed and is just the removal of this piece early in the second call:
Code: Select all
/*  && !(typ == TYPE_INTERRUPT && (cd->extra_ability & EA_MANA_SOURCE))*/