A good way to sort and present commands...
#1

I'm posting this in this board because it seems to be the one thing I see overlooked quite often on people's game-modes. People spend months, years even, developing a nice game mode and when it comes down to the /commands command, people end up just either listing out a bunch of text on a client message or they barely sort it out in a dialog.

So, I'm here to ask; how can we improve this small experience? Quite frankly, I find sending a ton of client messages to be VERY dull and a huge pain in the ass for some people with a "pagesize" of 10 and not something a little larger. I also despise dialogs and rarely use them. The only other option would be text-draws, which I'm quite fond of and have no problem presenting my commands to a user via a TD. However, this is where the discussion comes in: how would one do that?

I was thinking of having two MySQL tables, commands and commandCategories. Obviously, commands need to be sorted by categories for ease of use. Then they need a table of their own so you can have pertinent information with each command and its description. Here are the two table structures I had planned out:

commandCategories
- id, categoryName[], parentCategoryID

commands
- id, command[], description[], categoryID, type, value1, value2

I think the first structure is pretty self-explanatory. The last field allows a category to have a sub-category. As for the second structure, you have the command ID, the command itself, and a description. The "type" would be, for example, who can use the command (i.e. if it's a cop-only command or an admin-only command, etc). Value1 and value2 are just additional integer slots in-case any additional data needs to be stored (like a faction ID, for example).

When it comes to listing the commands, that's no big deal. However, the question is, which commands would you list? Would you load up every single command in a particular category and see if the user can access it according to the command type and the value of the value1/2 fields...?

How would YOU do this?
Reply
#2

Frankly, I would list up the most COMMON commands that will help the user on his gameplay.
I would show him commands that show up more commands, for example: /jobhelp which lists even more commands.
I've seen many servers that flood my chat with a list of commands which is really unorganized and you can't see half of the commands(If not more).
Reply
#3

I'd just stay away from commands as much as possible. You can put a bunch of stuff under event handlers. Key presses, entering a checkpoint, entering an area, entering a pickup, ... You name it.

If you still have lots of commands then it might be wise to install some sort of search feature. Something like '/help house' or '/help car'.
Reply
#4

I agree Vince. I hate commands. Honestly, my mode doesn't have that many so maybe I'll just put them in a dialog and not even worry about making some crazy dynamic system for it!
Reply
#5

You simply CAN'T put everything on an event. Some things HAVE to be commands. I do agree though that servers with 1000+ commands (believe me, they exist) can be VERY daunting - not just for a new player, but for even after playing for years you'd still never know them all.
Reply
#6

if your using a callback command handler

- Open AMX

- Take all cmd_%s out and irccmd_%s public's out

- Paste in dialog

/happydays
Reply
#7

I know, that's why I said 'as much as possible'. However, some (most?) servers still employ the /enter /exit mechanism while that could easily be assigned to a checkpoint, pickup or keypress. Also things like /engine, /trunk, /inventory, ... can be assigned to event handlers.
Reply
#8

I know one thing that cuts down on a lot of different commands would be a single command with multiple syntaxes. Like, instead of /lockvehicle, /unlockvehicle, /opentrunk, and /closetrunk you could do it all via /vehicle (un)lock.

Just about every command in my mode that has multiple "pieces" can be used from the same command. Rather than having /gotols, /gotosf, /gotolv, there's simply /teleport with a list of places to choose from (i.e. /teleport lspd).

Nevertheless, I do want to see what else I can get rid of command-wise. Thanks guys.
Reply
#9

It would be cool to start with generic verbs, such as "give, set, take, add, remove, ..".

The /give command, for example, could support various sorts of arguments:
  • /give <who> <what>
  • /give <id/name> <what>, <what>, ..
  • /give slice $200
  • /give slice infernus
  • /give slice hp, armour, deagle, grenade, $20000
Same with /set:
  • /set slice hp 200
  • /set slice armour 50
  • /set slice ammo mp5 5000
  • /set slice name slice9000
Optionally, without a player:
  • /set weather 20
  • /set gravity 0.06
I suppose you could have a generic function detecting what, for example:
(disclaimer: untested code)
pawn Code:
enum E_THINGS_TYPES {
    THING_TYPE_WEAPON,
    THING_TYPE_MONEY,
    THING_TYPE_VEHICLE,
    THING_TYPE_HEALTH,
    THING_TYPE_ARMOUR,
    THING_TYPE_JETPACK
};

stock GetThingFromString(const input[], &type, &thing) {
    new id;
   
    if (input[0] == '$') {
        type = THING_TYPE_MONEY;
        thing = strval(input[1]);
    } else if (!strcmp(input, "health")) {
        type = THING_TYPE_HEALTH;
        thing = strval(input[6]);
    } else if (!strcmp(input, "armour")) {
        type = THING_TYPE_ARMOUR;
        thing = strval(input[6]);
    } else if (!strcmp(input, "jetpack")) {
        type = THING_TYPE_JETPACK;
    } else if (GetWeaponIdFromString(input, id)) {
        type = THING_TYPE_WEAPON
        thing = id;
    } else if (GetVehicleIdFromString(input, id)) {
        type = THING_TYPE_VEHICLE
        thing = id;
    } else {
        // failed
        return 0;
    }
   
    // success
    return 1;
}

stock GivePlayerThing(type, thing) {
    switch (type) {
        case THING_TYPE_HEALTH: {
            SetPlayerHealth(playerid, float(thing));
        }
       
        // etc..
    }
}

// Usage:
new type, thing;

if (GetThingFromString(input, type, thing)) {
    switch (type) {
        // prevent some things
        // the best way is probably the other way around - only allow certain things
        case THING_TYPE_VEHICLE: {
            // ERROR: you're not allowed to spawn a vehicle
        }
       
        default: {
            GivePlayerThing(playerid, type, thing);
        }
    }
}
Reply
#10

I came up with this, feel free to complete/use it:
pawn Code:
#include <a_samp>

main() {
    new str[128], type, which, qty;

    GetThingFromString("nothing", type, which, qty);
    GetStringFromThing(str, type, which, qty), print(str);
    GetThingFromString("hp 20", type, which, qty);
    GetStringFromThing(str, type, which, qty), print(str);
    GetThingFromString("armour 50", type, which, qty);
    GetStringFromThing(str, type, which, qty), print(str);
    GetThingFromString("deagle 2000", type, which, qty);
    GetStringFromThing(str, type, which, qty), print(str);
    GetThingFromString("mp5", type, which, qty);
    GetStringFromThing(str, type, which, qty), print(str);
    GetThingFromString("infernus", type, which, qty);
    GetStringFromThing(str, type, which, qty), print(str);
    GetThingFromString("$9000", type, which, qty);
    GetStringFromThing(str, type, which, qty), print(str);
    GetThingFromString("BAM BOM HELLO", type, which, qty);
    GetStringFromThing(str, type, which, qty), print(str);
   
    new idx = 0, i = 0;
   
    while (GetThingFromString("hp 100, armour 50, mp5, deagle, infernus", type, which, qty, idx)) {
        GetStringFromThing(str, type, which, qty);
        printf("thing %d: %s", i++, str);
    }
}

enum e_THING_TYPES {
    THING_INVALID = -1,
   
    THING_NOTHING,
    THING_HEALTH,
    THING_ARMOUR,
    THING_WEAPON,
    THING_VEHICLE,
    THING_MONEY,
   
    // not implemented:
    THING_AMMO,
    THING_JETPACK,
    THING_SKIN,
    THING_COLOR,
    THING_FIGHTING_STYLE
}

static stock const gs_ThingName[][] = {"nothing", "health", "armour", "weapon", "vehicle", "money"};

static stock const gs_WeaponSearchNames[48][20 char] = {
    {!"fists"              }, {!"brassknuckles"     }, {!"golfclub"         }, //  0,  1,  2
    {!"nightsticknitestick"}, {!"knife"             }, {!"bat"              }, //  3,  4,  5
    {!"shovel"             }, {!"poolcue"           }, {!"katana"           }, //  6,  7,  8
    {!"chainsaw"           }, {!"dildo"             }, {!"vibrator"         }, //  9, 10, 11
    {!"vibrator"           }, {!"vibrator"          }, {!"flowers"          }, // 12, 13, 14
    {!"cane"               }, {!"grenades"          }, {!"teargas"          }, // 15, 16, 17
    {!"molotov"            }, {!""                  }, {!""                 }, // 18,      
    {!""                   }, {!"colt459mm"         }, {!"silencedpistol9mm"}, //     22, 23
    {!"deserteagledeagle"  }, {!"pumpregularshotgun"}, {!"sawnssawnoffs"    }, // 24, 25, 26
    {!"combatspas12"       }, {!"mac10uzi"          }, {!"mp5"              }, // 27, 28, 29
    {!"ak47ak-47"          }, {!"m4"                }, {!"tec9"             }, // 30, 31, 32
    {!"counrtyriflecuntgun"}, {!"sniperrifle"       }, {!"rocketlauncher"   }, // 33, 34, 35
    {!"heatseekingrpg"     }, {!"flamethrower"      }, {!"minigun"          }, // 36, 37, 38
    {!"satchel"            }, {!"detonator"         }, {!"spraycan"         }, // 39, 40, 41
    {!"extinguisher"       }, {!"camera"            }, {!"nightvision"      }, // 42, 43, 44
    {!"infrared"           }, {!"parachute"         }, {!"pistol"           }  // 45, 46, 47
};

static stock const gs_VehicleNames[212][18 char] = {
    {!"Landstalker"     }, {!"Bravura"        }, {!"Buffalo"          }, {!"Linerunner"       }, // 400, 401, 402, 403
    {!"Perrenial"       }, {!"Sentinel"       }, {!"Dumper"           }, {!"Firetruck"        }, // 404, 405, 406, 407
    {!"Trashmaster"     }, {!"Stretch"        }, {!"Manana"           }, {!"Infernus"         }, // 408, 409, 410, 411
    {!"Voodoo"          }, {!"Pony"           }, {!"Mule"             }, {!"Cheetah"          }, // 412, 413, 414, 415
    {!"Ambulance"       }, {!"Leviathan"      }, {!"Moonbeam"         }, {!"Esperanto"        }, // 416, 417, 418, 419
    {!"Taxi"            }, {!"Washington"     }, {!"Bobcat"           }, {!"Mr Whoopee"       }, // 420, 421, 422, 423
    {!"BF Injection"    }, {!"Hunter"         }, {!"Premier"          }, {!"Enforcer"         }, // 424, 425, 426, 427
    {!"Securicar"       }, {!"Banshee"        }, {!"Predator"         }, {!"Bus"              }, // 428, 429, 430, 431
    {!"Rhino"           }, {!"Barracks"       }, {!"Hotknife"         }, {!"Trailer 1"        }, // 432, 433, 434, 435
    {!"Previon"         }, {!"Coach"          }, {!"Cabbie"           }, {!"Stallion"         }, // 436, 437, 438, 439
    {!"Rumpo"           }, {!"RC Bandit"      }, {!"Romero"           }, {!"Packer"           }, // 440, 441, 442, 443
    {!"Monster"         }, {!"Admiral"        }, {!"Squalo"           }, {!"Seasparrow"       }, // 444, 445, 446, 447
    {!"Pizzaboy"        }, {!"Tram"           }, {!"Trailer 2"        }, {!"Turismo"          }, // 448, 449, 450, 451
    {!"Speeder"         }, {!"Reefer"         }, {!"Tropic"           }, {!"Flatbed"          }, // 452, 453, 454, 455
    {!"Yankee"          }, {!"Caddy"          }, {!"Solair"           }, {!"RC Van"           }, // 456, 457, 458, 459
    {!"Skimmer"         }, {!"PCJ-600"        }, {!"Faggio"           }, {!"Freeway"          }, // 460, 461, 462, 463
    {!"RC Baron"        }, {!"RC Raider"      }, {!"Glendale"         }, {!"Oceanic"          }, // 464, 465, 466, 467
    {!"Sanchez"         }, {!"Sparrow"        }, {!"Patriot"          }, {!"Quad"             }, // 468, 469, 470, 471
    {!"Coastguard"      }, {!"Dinghy"         }, {!"Hermes"           }, {!"Sabre"            }, // 472, 473, 474, 475
    {!"Rustler"         }, {!"ZR-350"         }, {!"Walton"           }, {!"Regina"           }, // 476, 477, 478, 479
    {!"Comet"           }, {!"BMX"            }, {!"Burrito"          }, {!"Camper"           }, // 480, 481, 482, 483
    {!"Marquis"         }, {!"Baggage"        }, {!"Dozer"            }, {!"Maverick"         }, // 484, 485, 486, 487
    {!"News Chopper"    }, {!"Rancher"        }, {!"FBI Rancher"      }, {!"Virgo"            }, // 488, 489, 490, 491
    {!"Greenwood"       }, {!"Jetmax"         }, {!"Hotring"          }, {!"Sandking"         }, // 492, 493, 494, 495
    {!"Blista"          }, {!"Police Maverick"}, {!"Boxville"         }, {!"Benson"           }, // 496, 497, 498, 499
    {!"Mesa"            }, {!"RC Goblin"      }, {!"Hotring A"        }, {!"Hotring B"        }, // 500, 501, 502, 503
    {!"Bloodring Banger"}, {!"Rancher"        }, {!"Super GT"         }, {!"Elegant"          }, // 504, 505, 506, 507
    {!"Journey"         }, {!"Bike"           }, {!"Mountain Bike"    }, {!"Beagle"           }, // 508, 509, 510, 511
    {!"Cropdust"        }, {!"Stuntplane"     }, {!"Tanker"           }, {!"Roadtrain"        }, // 512, 513, 514, 515
    {!"Nebula"          }, {!"Majestic"       }, {!"Buccaneer"        }, {!"Shamal"           }, // 516, 517, 518, 519
    {!"Hydra"           }, {!"FCR-900"        }, {!"NRG-500"          }, {!"HPV1000"          }, // 520, 521, 522, 523
    {!"Cement Truck"    }, {!"Tow Truck"      }, {!"Fortune"          }, {!"Cadrona"          }, // 524, 525, 526, 527
    {!"FBI Truck"       }, {!"Willard"        }, {!"Forklift"         }, {!"Tractor"          }, // 528, 529, 530, 531
    {!"Combine"         }, {!"Feltzer"        }, {!"Remington"        }, {!"Slamvan"          }, // 532, 533, 534, 535
    {!"Blade"           }, {!"Freight"        }, {!"Streak"           }, {!"Vortex"           }, // 536, 537, 538, 539
    {!"Vincent"         }, {!"Bullet"         }, {!"Clover"           }, {!"Sadler"           }, // 540, 541, 542, 543
    {!"Firetruck LA"    }, {!"Hustler"        }, {!"Intruder"         }, {!"Primo"            }, // 544, 545, 546, 547
    {!"Cargobob"        }, {!"Tampa"          }, {!"Sunrise"          }, {!"Merit"            }, // 548, 549, 550, 551
    {!"Utility"         }, {!"Nevada"         }, {!"Yosemite"         }, {!"Windsor"          }, // 552, 553, 554, 555
    {!"Monster"         }, {!"Monster"        }, {!"Uranus"           }, {!"Jester"           }, // 556, 557, 558, 559
    {!"Sultan"          }, {!"Stratum"        }, {!"Elegy"            }, {!"Raindance"        }, // 560, 561, 562, 563
    {!"RC Tiger"        }, {!"Flash"          }, {!"Tahoma"           }, {!"Savanna"          }, // 564, 565, 566, 567
    {!"Bandito"         }, {!"Freight Flat"   }, {!"Streak Carriage"  }, {!"Kart"             }, // 568, 569, 570, 571
    {!"Mower"           }, {!"Duneride"       }, {!"Sweeper"          }, {!"Broadway"         }, // 572, 573, 574, 575
    {!"Tornado"         }, {!"AT-400"         }, {!"DFT-30"           }, {!"Huntley"          }, // 576, 577, 578, 579
    {!"Stafford"        }, {!"BF-400"         }, {!"Newsvan"          }, {!"Tug"              }, // 580, 581, 582, 583
    {!"Trailer 3"       }, {!"Emperor"        }, {!"Wayfarer"         }, {!"Euros"            }, // 584, 585, 586, 587
    {!"Hotdog"          }, {!"Club"           }, {!"Freight Carriage" }, {!"Trailer 3"        }, // 588, 589, 590, 591
    {!"Andromada"       }, {!"Dodo"           }, {!"RC Cam"           }, {!"Launch"           }, // 592, 593, 594, 595
    {!"Police LS"       }, {!"Police SF"      }, {!"Police LV"        }, {!"Police Ranger"    }, // 596, 597, 598, 599
    {!"Picador"         }, {!"S.W.A.T. Van"   }, {!"Alpha"            }, {!"Phoenix"          }, // 600, 601, 602, 603
    {!"Glendale"        }, {!"Sadler"         }, {!"Luggage Trailer A"}, {!"Luggage Trailer B"}, // 604, 605, 606, 607
    {!"Stair Trailer"   }, {!"Boxville"       }, {!"Farm Plow"        }, {!"Utility Trailer"  }  // 608, 609, 610, 611
};

static stock FindWeaponID(const input[]) {
    new search[20], i = 0;
   
    while (0 < input[i] <= ' ') {
        i++;
    }
   
    if (!input[i]) {
        return -1;
    }
   
    while (0 < input[i] > ' ' && input[i] != ',') {
        i++;
    }
   
    strmid(search, input, 0, i);
   
    for (i = 0; i < sizeof(gs_WeaponSearchNames); i++) {
        if (!gs_WeaponSearchNames[i][0]) {
            continue;
        }
       
        if (strfind(gs_WeaponSearchNames[i], search, true) != -1) {
            return i;
        }
    }
   
    return -1;
}

static stock FindVehicleID(const input[]) {
    new match = INVALID_VEHICLE_ID, match_len;
   
    for (new i = 0; i < sizeof(gs_VehicleNames); i++) {
        if (strfind(gs_VehicleNames[i], input, true) != -1) {
            if (match != INVALID_VEHICLE_ID) {
                new len = strlen(gs_VehicleNames[i]);
               
                if (len < match_len) {
                    match = 400 + i;
                    match_len = len;
                }
            } else {
                match = 400 + i;
                match_len = strlen(gs_VehicleNames[i]);
            }
        }
    }
   
    return match;
}

stock GetThingFromString(const input[], &type, &which, &quantity, &index = 0) {
    new id;
   
    type = -1;
   
    while (0 < input[index] <= ' ') {
        index++;
    }
   
    if (!input[index]) {
        return 0;
    }
   
    if (!strcmp(input, "nothing", true)) {
        type = THING_NOTHING;
    } else if (!strcmp(input[index], "health", true, 6) ||
               !strcmp(input[index], "hp", true, 2)) {
        type = THING_HEALTH;
       
        if (tolower(input[index + 1]) == 'p') {
            index += 2;
        } else {
            index += 6;
        }
       
        quantity = strval(input[index]);
    } else if (!strcmp(input[index], "armour", true, 6) ||
               !strcmp(input[index], "armor", true, 5)) {
        type = THING_ARMOUR;
       
        if (tolower(input[index + 4]) == 'r') {
            index += 5;
        } else {
            index += 6;
        }
       
        quantity = strval(input[index]);
    } else if (input[index] == '$') {
        type = THING_MONEY;
       
        quantity = strval(input[index + 1]);
    } else if (-1 != (id = FindWeaponID(input[index]))) {
        type = THING_WEAPON;
        which = id;
        quantity = -1;
       
        while (input[index] && input[index] > ' ' && input[index] != ',') {
            index++;
        }
       
        if (input[index] && input[index] != ',') {
            quantity = strval(input[index]);
        }
    } else if (INVALID_VEHICLE_ID != (id = FindVehicleID(input[index]))) {
        type = THING_VEHICLE;
        which = id;
        quantity = -1;
    } else {
        return 0;
    }
   
    while (input[index] && input[index] != ',') {
        index++;
    }
   
    if (input[index] == ',') {
        index++;
    }
   
    return 1;
}

stock GivePlayerThing(playerid, type, which, quantity = 0) {
    // TODO
}

stock GetStringFromThing(output[], thing, which, quantity = 0, maxlength = sizeof(output)) {
    output[0] = '\0';
   
    switch (thing) {
        case THING_NOTHING: {
            strcat(output, "Nothing", maxlength);
        }
           
        case THING_HEALTH: {
            format(output, maxlength, "%d health", quantity);
        }
           
        case THING_ARMOUR: {
            format(output, maxlength, "%d armour", quantity);
        }
           
        case THING_WEAPON: {
            GetWeaponName(which, output, maxlength);
           
            if (quantity > -1) {
                format(output, maxlength, "%s with %d ammo", output, quantity);
            }
        }
           
        case THING_VEHICLE: {
            which -= 400;
           
            if (0 <= which < sizeof(gs_VehicleNames)) {
                strunpack(output, gs_VehicleNames[which], maxlength);
            } else {
                strcat(output, "Invalid vehicle", maxlength);
            }
        }
           
        case THING_MONEY: {
            format(output, maxlength, "$%d", quantity);
        }
           
        default: {
            strcat(output, "Invalid thing", maxlength);
        }
    }
}
Output:
Code:
[14:42:25] Nothing
[14:42:25] 20 health
[14:42:25] 50 armour
[14:42:25] Desert Eagle with 2000 ammo
[14:42:25] MP5
[14:42:25] Infernus
[14:42:25] $9000
[14:42:25] Invalid thing
[14:42:25] thing 0: 100 health
[14:42:25] thing 1: 50 armour
[14:42:25] thing 2: MP5
[14:42:25] thing 3: Desert Eagle
[14:42:25] thing 4: Infernus
Reply
#11

i see you declared those arrays with a ! in front of them, what does that do?
Reply
#12

It packs the strings.
Reply
#13

Could you explain what that does Slice? Your code has me pretty confused...
Reply
#14

pawn Code:
new type, which, qty;

// mp5 with 5000 ammo
GetThingFromString("mp5 5000", type, which, qty);

// type = THING_WEAPON
// which = WEAPON_MP5
// qty = 5000
Reply
#15

Oh! That's pretty clever. Thanks.
Reply
#16

I'm sorry, but I don't see any difference.
In addition to the harder scripting, wouldn't it just recreate the existing problem? You'd have 100 possibilities under /give, and 40 possibilities under /set, and I honestly don't see how is it easier to remember /give infernus than /v infernus, or /give mp5 than /w mp5. It's pretty nice, but in the end of the day you'd have to make a list of /set params and a list of /give params, and to present these list you'd use... a "big wall of text" client messages or dialog.
Reply
#17

Its more intuitive that way. "/give infernus" clearly states that an infernus will be given, but "/v infernus" doesnt that obvious information. What will it do with the infernus - create it, delete it or burn it like a medieval witch?
And the wall of text will be significantly smaller.
Reply
#18

Quote:
Originally Posted by Kar
View Post
if your using a callback command handler

- Open AMX

- Take all cmd_%s out and irccmd_%s public's out

- Paste in dialog

/happydays
or...

http://pastebin.com/USpNTbu7

Would be a good idea to use events other than plain commands.

Personally, I am not a fan of having /give [param] [val], it just doesn't feel right to me
Reply
#19

After reading this thread I came up with this idea for displaying admin commands using dialogs, dini and a little snippet out of gl_common.inc called 'token_by_delim', I'm currently running it on my server and it works great.

Using this method all your admin commands will automatically update into an organized dialog menu, this can easily be modified so that it works for all commands.

Put this in your /acmds command:
pawn Code:
ShowPlayerDialog(playerid, 666, DIALOG_STYLE_LIST, "Admin Commands", "Level 1 Commands \nLevel 2 Commands \nLevel 3 Commands \nLevel 4 Commands \nLevel 5 Commands \nConfig Commands \nOwner Commands", "Select", "Cancel");
OnDialog:

pawn Code:
OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    if(dialogid == 666)
    {
        if(response)
        {

            new
                File:pFile,
                line[256],
                var_from_line[128],
                Command[24],
                Level,
                index,
                count = -1,
                DisplayStr[128],
                aLVL = listitem + 1;
                        //change this destination to wherever you want
            pFile = fopen("AdminCommands.INI", filemode:io_read);
            if(!pFile)
                return 1;

            while(fread(pFile, line, 256) > 0)
            {
                index = 0;
                index = token_by_delim(line,var_from_line,'=',index);
                if(index == (-1)) continue;
                format(Command, 24, "%s\n", var_from_line);

                index = token_by_delim(line,var_from_line,' ',index+1);
                Level = strval(var_from_line);

                if(Level == aLVL)
                {
                    count++;
                    format(CMDCount[count], 24, "%s", Command);
                    strins(DisplayStr, Command, 0);
                }
            }
            fclose(pFile);
           
            ShowPlayerDialog(playerid, 667, DIALOG_STYLE_LIST, "Admin Commands", DisplayStr, "Select", "Back");
        }
        return 1;
    }
now create the ini file in the destination that you entered, the inside of the file should look like this:

Code:
freeze=2
kick=2
ban=4
setlevel=5
cmdlvl=5
and you need a command that checks the players admin level, here's mine:
pawn Code:
stock CheckAdmin(playerid, command[])
{
        //change these around to match your settings
    if(p_INFO[playerid][ADMIN] < dini_Int("AdminCommands.INI",command))
    {
        new
            p_STR[128];
        format(p_STR, 128, "ERROR: You need to be level %d to use this command.", dini_Int("AdminCommands.INI",command));
        SendClientMessage(playerid, WHITE, p_STR);
        return 0;
    }
    return 1;
}
put that under every admin command you have (see below).

I added a command that lets you change the level of admin commands while ingame:
pawn Code:
dcmd_cmdlvl(playerid, params[])
{
        //this string should be spelt same as the one in your AdminCommands.INI file
    if(!CheckAdmin(playerid, "cmdlvl"))
        return 1;

    new
        CMD[24],
        LVL;
    if(sscanf(params, "s[24]i", CMD, LVL))
    {
        SendClientMessage(playerid, WHITE, "USAGE: /cmdlvl <Command> <Level>");
        return 1;
    }
    dini_IntSet("AdminCommands.INI",CMD, LVL);
    new
        pSTR[128];
    format(pSTR, 128, "You have set the command, \"%s\"'s level requirement to %d.", CMD, LVL);
    SendClientMessage(playerid, YELLOW, pSTR);
    return 1;
}
You will also need this snippet from gl_commons:

pawn Code:
stock token_by_delim(const string[], return_str[], delim, start_index)
{
    new x=0;
    while(string[start_index] != EOS && string[start_index] != delim) {
        return_str[x] = string[start_index];
        x++;
        start_index++;
    }
    return_str[x] = EOS;
    if(string[start_index] == EOS) start_index = (-1);
    return start_index;
}
I was originally going to use SQLite and save all the commands to a table, this way I could add a description for each command and have it all accessible through the dialog, but this is a lot easier and pretty simple.

You might have to edit this code to get it to work as I have just copied it straight from my gm.
Reply


Forum Jump:


Users browsing this thread: 10 Guest(s)