[Tutorial] Unlimited dialogs
#1

Introduction

As stated in wiki, the max ID of a dialog is 32767. This is more than enough in most cases, but sometimes you might need to exceed the limit. In other cases you might simply want to group your dialog types. Now - nobody said that the id's can't be reusable.

Defining the dialogs

Let's start with defining dialog groups

pawn Code:
enum e_DIALOG_BASIC {
    DIALOG_FOO,
    DIALOG_BAR
}

enum e_DIALOG_ADVANCED {
    DIALOG_FOOBAR,
    DIALOG_BARFOO
}
(notice the small letter in enum label - this causes it to be a weak tag)

Wrapping ShowPlayerDialog

pawn Code:
stock ShowPlayerDialogEx(playerid, dialogid, style, caption[], info[], button1[], button2[], tagDialogGroup = tagof dialogid) {
    switch(tagDialogGroup) {
        case (tagof e_DIALOG_ADVANCED): {
            state dialogHandler:e_DIALOG_ADVANCED;
        }
        default: {
            state dialogHandler:e_DIALOG_BASIC;
        }
    }
   
    return ShowPlayerDialog(playerid, dialogid, style, caption, info, button1, button2);
}
Instead of messy switch statement in OnDialogResponse we can harness the power of automata (which uses same opcodes as switch, but looks fancier and cleaner at the same time). tagDialogGroup will grab tag automatically from dialogid argument and then set the state to the one we need (there is no possibility of assigning variable to state, and conditional state is not optimal in this case).

Adding commands and handlers

pawn Code:
CMD:basic(id, params[]) {
    return ShowPlayerDialogEx(id, DIALOG_FOO, DIALOG_STYLE_MSGBOX, "Basic text", "Basic info", "Ok", "Have fun");
}
CMD:adv(id, params[]) {
    return ShowPlayerDialogEx(id, DIALOG_FOOBAR, DIALOG_STYLE_MSGBOX, "Adv text", "Adv info", "Ok", "Have fun");
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[]) <> {
    printf("Dialog %d of BASIC", dialogid);
    return 1;
}
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[]) <dialogHandler:e_DIALOG_ADVANCED> {
    printf("Dialog %d of Advanced", dialogid);
    return 1;
}
DIALOG_FOO has e_DIALOG_BASIC tag, and DIALOG_FOOBAR has e_DIALOG_ADVANCED tag. State values are equal to our tags (and e_DIALOG_BASIC is handled as default group). Everything works, let's celebrate! Using only just two groups we can handle up to 65536 dialogs. Whenever you want, you can add another group by adding 3 lines in ShowPlayerDialogEx, another enumerator and additional handler.

God damn it

Just as you are about to open the champagne, sudden realization hits you - we don't have any protection from cheaters and party crashers. That's not good. Usually it's handled by fixes.inc, but now it's not!

Let's fix it

We have two ways of doing this. One would require adding protection in every dialogresponse handler, and the other one requires us to get rid of magic state machines. The second option is easier, and that's what we are going to do. Good bye tidy code!

Fixes.inc is written by people who know pawn inside-out, so we will use it, while adding additional functionality.

pawn Code:
new PlayerDialogGroup[MAX_PLAYERS];
Each player needs to have his current dialog group stored somewhere - earlier we dealt with it using state machines, now we have to do it manually.

pawn Code:
stock ShowPlayerDialogEx(playerid, dialogid, style, caption[], info[], button1[], button2[], tagDialogGroup = tagof dialogid) {
    PlayerDialogGroup[playerid] = tagDialogGroup;
    return ShowPlayerDialog(playerid, dialogid, style, caption, info, button1, button2);
}
We are assigning the tag of dialogid directly. In fact ShowPlayerDialogEx looks nicer now, and could be transformed into macro

pawn Code:
#define ShowPlayerDialogEx(%1,%2,%3,%4,%5,%6,%7) (PlayerDialogGroup[%1] = tagof(%2), ShowPlayerDialog(%1,%2,%3,%4,%5,%6,%7))
Your choice.

pawn Code:
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[]) {
    switch(PlayerDialogGroup[playerid]) {
        case (tagof e_DIALOG_ADVANCED): {
            printf("Dialog %d of ADVANCED", dialogid);
        }
        default: {
            printf("Dialog %d of BASIC", dialogid);
        }
    }
    return 1;
}
And that's it. We can now use best of both worlds. Your whole additional logic (checking the dialogid, listitem selected, etc.) goes inside specific case. Additionally you can add some actions which will fire up on multiple dialog groups, or all of them.

That's all folks!
Reply
#2

pretty nice. But why not

Code:
new p_currentDialog[MAX_PLAYERS];

enum eDialogs
{
    dRegistration,
    dLogin

// ... unlimited ! (technically limited - 2147483647)
};

stock showDialog(playerid, dialogid, type, head[], body[], left[], right[])
{
    p_currentDialog[playerid] = dialogid;
    ShowPlayerDialog(playerid, 0, type, head, body, left, right);
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    new const id = p_currentDialog[playerid];
    p_currentDialog[playerid] = 0;
    
    switch( id )
    {
        case dRegistration:
        {
            // do registration things
        }
        case dLogin:
        {
            // do login things
        }
    }

    return 0;
}
Reply
#3

@Y_Less: Well, in fact enums aren't used there at all. Take a look at this code (not quite valid, but works can be parsed with -a flag)
pawn Code:
enum A {}
enum B {}

main() {
    state s:A;
    Fnc();
    state z:B;
    Fnck();
}

Fnc() <s:A> {
    #emit STATEA
}
Fnc() <s:B> {
    #emit STATEB
}

Fnck() <z:B> {
    #emit STATEB
}
Fnck() <z:A> {
    #emit STATEA
}
It simply produces
pawn Code:
load.pri 4  ; Fnc
    switch 3
l.3     ; 20
    casetbl
    case 2 0
    case 1 1
    case 2 2

    load.pri 8  ; Fnck
    switch 6
l.6     ; 4c
    casetbl
    case 2 0
    case 1 4
    case 2 5
They are substituted in order of declaraction with numbers. Even "__" as state name will get parsed, but I can't find official rules for their labels.

@theYiin: Yup, I thought about this version (which is very clever by the way), but then I changed my main goal to grouping (so you don't have to do something like)

pawn Code:
enum g_Main {
d_n_1,
d_n_2,
d_n_3
}

enum g_Additional {
d_a_1 = 3,
d_a_2,
d_a_3
}
Reply
#4

Quote:
Originally Posted by Y_Less
View Post
Another thought: Given that there are only 1000 players, there can only be 1000 dialogs on display at once maximum, so why not use playerid instead and dispense with dialog IDs entirely?
do you mean http://forum.sa-mp.com/showthread.ph...36#post2667336 ?
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)