Page 1 of 1

Re: [confirmed] Demonic Pact dialog

PostPosted: 01 Dec 2021, 15:57
by drool66
Confirmed. We can fix this in individual cards with DLG_OMIT_ILLEGAL, but that's not exactly copacetic. Fixing that probably involves working with the windows interface, which I don't know a lot about.

Another idiosyncrasy - if a dialog option's ai_priority <= 0, AI can just run through the options without actually choosing that one, like if it has a Demonic Pact with the first three options already chosen. Changing the fourth option's ai_priority to 1 disallows this behavior. I can speculate on how to get dialog_ai() to handle non-positive values for ai_priority.

Fixed both issues for Demonic Pact, unpushed, but leaving this open for the sake of the two issues above.

Re: [confirmed] Demonic Pact dialog

PostPosted: 01 Dec 2021, 16:15
by Korath
See the WM_INITDIALOG handler in Shandalar's hook_dlgproc_do_dialog() for how I fixed the first issue over there. (Basically, if the first option is grayed out when the dialog is created, send it a "home" keypress. And, on reflection, it's probably not even necessary to check if the first option is grayed out, either - just always send the key.)

Having at least one option with positive ai_priority, or being cancellable, is part of DIALOG()'s documented preconditions. I suppose you could add a popup to complain if that's untrue, though whether that's more irritating for end-users than the current wrong behavior would vary from card to card. Or you could just give each legal option an even weighting.

Re: [confirmed] Demonic Pact dialog

PostPosted: 03 Dec 2021, 06:35
by drool66
See the WM_INITDIALOG handler in Shandalar's hook_dlgproc_do_dialog() for how I fixed the first issue over there. (Basically, if the first option is grayed out when the dialog is created, send it a "home" keypress. And, on reflection, it's probably not even necessary to check if the first option is grayed out, either - just always send the key.)
Done,unpushed, though I just made it dependent on "(who_sees = EXE_DWORD(0x736ad8)) == HUMAN"; I don't see a separate param in ML for "who_chooses." Does it even need to do that?

Re: [fixed] Demonic Pact dialog

PostPosted: 03 Dec 2021, 16:34
by Korath
It's harmless to send it regardless of who_sees - wndproc_BigCardChoiceClass(), which eventually gets the message, ignores WM_KEYDOWN unless player 0 is choosing.

do_dialog()@0x471EB0 sends !who_chooses as the last parameter to raw_do_dialog()@0x471B60, which sets it in one of the fields of the struct dialog_args_t { int bigcard_player; int bigcard_card; int smallcard_player; int smallcard_card; char* prompt; int human_chooses; } it passes as the dialog's dwInitParam, becoming lparam in dlgproc_do_dialog()'s WM_INITDIALOG handler. A pointer to the struct gets stored in static_dlg_args, which is at 0x607490 in Manalink. What leads you to think 0x736ad8 is relevant?

Re: [fixed] Demonic Pact dialog

PostPosted: 03 Dec 2021, 21:37
by drool66
What leads you to think 0x736ad8 is relevant?
Not having everything mapped properly. I deduced (wrongly) from the lines in do_dialog():
Code: Select all
      v10 = a2 == dword_736AD8;
      v13 = sub_471B60(a1, HIDWORD(a2), a3, a4, HIDWORD(a4), (int)&byte_60A690, v10);   //raw_do_dialog()
Thank you for the address, but I can't get it to work properly. Here's what I have:
Code: Select all
typedef struct
{
  int bigcard_player;
  int bigcard_card;
  int smallcard_player;
  int smallcard_card;
  const char* prompt;
  int human_chooses;
} dialog_args_t;

#define option_Layout      EXE_DWORD(0x787260)
#define hwnd_FullCardClass   EXE_PTR_VOID(0x715ca0)
#define static_dlg_args      EXE_TYP_PTR(dialog_args_t, 0x607490)

INT_PTR __stdcall dlgproc_do_dialog_hook(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  if (msg == (option_Layout == 2 ? WM_RBUTTONDBLCLK : WM_RBUTTONDOWN))
   {
     SendMessageA(hwnd_FullCardClass, WM_COMMAND, 1, 0);
     return 0;
   }

  // move to first legal choice by sending a home key press
  if( static_dlg_args->human_chooses ){
     int num_choices = SendDlgItemMessage(hwnd, 1021, WM_USER, 0, 0);
     if (num_choices > 0)
         SendDlgItemMessage(hwnd, 1021, WM_KEYDOWN, VK_HOME, 0);
  }

  return EXE_STDCALL_FN(INT_PTR, 0x4aa090, HWND, UINT, WPARAM, LPARAM)(hwnd, msg, wparam, lparam);   // dlgproc_do_dialog()
}
I've also tried this kind of thing:
Code: Select all
typedef struct
{
  int bigcard_player;
  int bigcard_card;
  int smallcard_player;
  int smallcard_card;
  const char* prompt;
  int human_chooses;
} dialog_args_t;

#define option_Layout      EXE_DWORD(0x787260)
#define hwnd_FullCardClass   EXE_PTR_VOID(0x715ca0)
#define static_dlg_args      EXE_TYP_PTR(dialog_args_t, 0x607490)

INT_PTR __stdcall dlgproc_do_dialog_hook(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  if (msg == (option_Layout == 2 ? WM_RBUTTONDBLCLK : WM_RBUTTONDOWN))
   {
     SendMessageA(hwnd_FullCardClass, WM_COMMAND, 1, 0);
     return 0;
   }

  if( msg == WM_INITDIALOG ){
     INT_PTR rv = EXE_STDCALL_FN(INT_PTR, 0x4aa090, HWND, UINT, WPARAM, LPARAM)(hwnd, msg, wparam, lparam);   // dlgproc_do_dialog()
     // move to first legal choice by sending a home key press
     if( static_dlg_args->human_chooses ){
        int num_choices = SendDlgItemMessage(hwnd, 1021, WM_USER, 0, 0);
        if (num_choices > 0)
         SendDlgItemMessage(hwnd, 1021, WM_KEYDOWN, VK_HOME, 0);
     }
     return rv;
   }

  return EXE_STDCALL_FN(INT_PTR, 0x4aa090, HWND, UINT, WPARAM, LPARAM)(hwnd, msg, wparam, lparam);   // dlgproc_do_dialog()
}
On the other hand, like you say simply unconditionally firing "SendDlgItemMessage(hwnd, 1021, WM_KEYDOWN, VK_HOME, 0)" works and doesn't seem to have any repercussions as far as I can tell.

Re: [fixed] Demonic Pact dialog

PostPosted: 03 Dec 2021, 22:09
by Korath
Second kind of thing is almost right. (The first isn't. A DLGPROC gets all kinds of messages; you only want to respond to WM_INITDIALOG, and only after what the original does - including setting up static_dlg_args in the first place - is complete.)

The problem is that you haven't defined static_dlg_args correctly. What you have is saying that there's a dialog_args_t stored starting at address 0x607490, and that your symbol static_dlg_args is a pointer to it. It's actually stored on the stack, as one of the local variables in raw_do_dialog(), and what's stored at 0x607490 is its address. (That's why I can get away with tacking extra data members onto the end of the structure in Shandalar just by replacing raw_do_dialog().) So you want to make static_dlg_args to be an alias of the pointer at 0x607490, and the way you do that is by #defining it as EXE_TYP(dialog_args_t*, 0x607490).

Omitting the check against dialog_args_t::human_chooses is harmless. Omitting the one against num_choices might not be; it's not completely clear what would happen with an unchoiced popup without it.

The value at 0x736ad8 looks like the equivalent of our HUMAN. It's set once to zero and then never changed again.

Re: [fixed] Demonic Pact dialog

PostPosted: 04 Dec 2021, 01:46
by drool66
Oh wow, thanks! That explains an awful lot.

EDIT: fixed in c4d64b4 & 41e1a37