Question about similarities between dcmd and ycmd+sscanf
#1

Alright, so in the LAdmin4v2 filterscript there are these two commands, /car and /giveweapon.
I like them because you can for example do /car inf, /car infernus, /car 411 and all three would spawn the Infernus, which means that you can put both the model name and the model id.
http://pastebin.com/F3KRi26V

It's the same for the giveweapon command, you can do /giveweapon 0 mini, /giveweapon 0 minigun, /giveweapon 0 38 and all three would give the player a minigun.

I've tried to recreate this kind using ycmd and sscanf but it does not work out for me. It would be nice if somebody could put together some ycmd/sscanf code for this (using the u param for player id/name) so I could get it to work because I don't really feel like putting strtok in the middle of my code.

Thanks in advance.

tl;dr need "/giveweapon [player id/name] [weapon id/name] [optional: ammo, default 500]"
and "/car [model id/name] [optional: color 1, default -1/random] [optional: color 2, default -1/random]"
using sscanf and ycmd.
Reply
#2

The command processor that you choose will not affect anything to do with the actual coding structure in particular. This kind of command relies on simple array and looping methods to find the right value for each weapon/vehicle model. As there is a set order in both, you are able to use arrays, because they are in ascending order from 400-611 and 0-46 with small or no breaks.

So basically for the vehicle system, you will want to set up your array to be compatible with both vehicle names and model ids. In order to determine whether the player's input is numerical or not, you can use SSCANF for an easy and understandable structure. SSCANF is technically essential if you're going to use optional parameters in any case.

So for the beginning, you will start with your vehicle names array:
pawn Code:
new VehicleNames[212][] =
{
    "Landstalker", "Bravura", "Buffalo", "Linerunner", "Pereniel", "Sentinel", "Dumper", "Firetruck", "Trashmaster", "Stretch", "Manana", "Infernus",
    "Voodoo", "Pony", "Mule", "Cheetah", "Ambulance", "Leviathan", "Moonbeam", "Esperanto", "Taxi", "Washington", "Bobcat", "Mr Whoopee", "BF Injection",
    "Hunter", "Premier", "Enforcer", "Securicar", "Banshee", "Predator", "Bus", "Rhino", "Barracks", "Hotknife", "Trailer", "Previon", "Coach", "Cabbie",
    "Stallion", "Rumpo", "RC Bandit", "Romero", "Packer", "Monster", "Admiral", "Squalo", "Seasparrow", "Pizzaboy", "Tram", "Trailer", "Turismo", "Speeder",
    "Reefer", "Tropic", "Flatbed", "Yankee", "Caddy", "Solair", "Berkley's RC Van", "Skimmer", "PCJ-600", "Faggio", "Freeway", "RC Baron", "RC Raider",
    "Glendale", "Oceanic", "Sanchez", "Sparrow", "Patriot", "Quad", "Coastguard", "Dinghy", "Hermes", "Sabre", "Rustler", "ZR3 50", "Walton", "Regina",
    "Comet", "BMX", "Burrito", "Camper", "Marquis", "Baggage", "Dozer", "Maverick", "News Chopper", "Rancher", "FBI Rancher", "Virgo", "Greenwood",
    "Jetmax", "Hotring", "Sandking", "Blista Compact", "Police Maverick", "Boxville", "Benson", "Mesa", "RC Goblin", "Hotring Racer A", "Hotring Racer B",
    "Bloodring Banger", "Rancher", "Super GT", "Elegant", "Journey", "Bike", "Mountain Bike", "Beagle", "Cropdust", "Stunt", "Tanker", "RoadTrain",
    "Nebula", "Majestic", "Buccaneer", "Shamal", "Hydra", "FCR-900", "NRG-500", "HPV1000", "Cement Truck", "Tow Truck", "Fortune", "Cadrona", "FBI Truck",
    "Willard", "Forklift", "Tractor", "Combine", "Feltzer", "Remington", "Slamvan", "Blade", "Freight", "Streak", "Vortex", "Vincent", "Bullet", "Clover",
    "Sadler", "Firetruck", "Hustler", "Intruder", "Primo", "Cargobob", "Tampa", "Sunrise", "Merit", "Utility", "Nevada", "Yosemite", "Windsor", "Monster A",
    "Monster B", "Uranus", "Jester", "Sultan", "Stratum", "Elegy", "Raindance", "RC Tiger", "Flash", "Tahoma", "Savanna", "Bandito", "Freight", "Trailer",
    "Kart", "Mower", "Duneride", "Sweeper", "Broadway", "Tornado", "AT-400", "DFT-30", "Huntley", "Stafford", "BF-400", "Newsvan", "Tug", "Trailer A", "Emperor",
    "Wayfarer", "Euros", "Hotdog", "Club", "Trailer B", "Trailer C", "Andromada", "Dodo", "RC Cam", "Launch", "Police Car (LSPD)", "Police Car (SFPD)",
    "Police Car (LVPD)", "Police Ranger", "Picador", "S.W.A.T. Van", "Alpha", "Phoenix", "Glendale", "Sadler", "Luggage Trailer A", "Luggage Trailer B",
    "Stair Trailer", "Boxville", "Farm Plow", "Utility Trailer"
};
Then you will need a function that retrieves model id from these names. As vehicles start at '400', the first element of your array (element 0) will correspond with 'Vehicle - 400'. (0 = 400 - 400).
pawn Code:
stock GetVehicleModelFromName(vehname[])
{
    for(new i = 0; i < sizeof(VehicleNames); i++)
    {
        if(strfind(VehicleNames[i], vehname, true) != -1) return i + 400;
    }
    return -1;
}
Then your command will be constructed from the array and function. With a bit of mixing with SSCANF, you are able to create the following:
pawn Code:
CMD:car(playerid, params[])
{
    new var, vehname[20], col1, col2;
    if(!sscanf(params, "iI(-1)I(-1)", var, col1, col2)) //Player entered a number.
    {
        if(!(400 <= var <= 611)) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid Vehicle Model, must be between 400 and 611.");
    }
    else if(!sscanf(params, "s[20]I(-1)I(-1)", vehname, col1, col2)) //Player entered a string
    {
        var = GetVehicleModelFromName(vehname);
        if(var == -1) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid vehicle name entered.");
    }
    else return SendClientMessage(playerid, 0xFF0000FF, "USAGE: /car [vehicle name/id] [Optional: color1] [Optional: color2]");
    if(col1 < 0) col1 = random(255);
    if(col2 < 0) col2 = random(255);
    new Float:X, Float:Y, Float:Z, Float:A, fstr[58];
    GetPlayerPos(playerid, X, Y, Z);
    GetPlayerFacingAngle(playerid, A);
    PutPlayerInVehicle(playerid, CreateVehicle(var, X, Y, Z + 1.0, A, col1, col2, -1), 0);
    format(fstr, sizeof(fstr), "You successfully created a %s (ID: %d)", VehicleNames[(var - 400)], var);
    SendClientMessage(playerid, 0xFFFF00FF, fstr);
    return 1;
}
--

With the weapons, they follow the same principles. An array for weapon names, and a function to retrieve.
pawn Code:
new GunNames[47][] =
{
    "Fist", "Brass Knuckles", "Golf Club", "Nightstick", "Knife", "Baseball Bat",
    "Shovel", "Pool Cue", "Katana", "Chainsaw", "Purple Dildo", "Dildo", "Vibrator",
    "Silver Vibrator", "Flowers", "Cane", "Grenade", "Tear Gas", "Molotov Cocktail",
    "null", "null", "null", "9mm", "Silenced 9mm", "Desert Eagle", "Shotgun", "Sawnoff Shotgun",
    "Combat Shotgun", "Micro SMG/Uzi", "MP5", "AK-47/AK47", "M4", "Tec-9/Tec9/Tec 9", "Country Rifle",
    "Sniper Rifle", "RPG/Rocket Launcher", "Heatseeker/Heat Seeker", "Flamethrower", "Minigun", "Satchel Charges",
    "Detonator", "Spraycan", "Fire Extinguisher", "Camera", "Night Vision Goggles", "Thermal Goggles",
    "Parachute"
};

stock GetWeaponIDFromName(weapname[])
{
    for(new i = 0; i < sizeof(GunNames); i++)
    {
        if(18 < i < 22) continue;
        if(strfind(GunNames[i], weapname, true) != -1) return i;
    }
    return -1;
}
Remember the input of the player only has to be found within the weapon names, so weapon names don't need to be perfect in this circumstance because we have a 'GetWeaponName' function with SA-MP. With SSCANF again, you can construct this:
pawn Code:
CMD:giveweapon(playerid, params[])
{
    new targetid, var, weapname[32], ammo;
    if(!sscanf(params, "uiI(500)", targetid, var, ammo)) //Player entered a number.
    {
        if(!(0 <= var <= 46) || (18 < var < 22)) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid Weapon ID, must be between 0 and 46.");
    }
    else if(!sscanf(params, "is[32]I(500)", targetid, weapname, ammo)) //Player entered a string.
    {
        var = GetWeaponIDFromName(weapname);
        if(var == -1) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid weapon name entered.");
    }
    else return SendClientMessage(playerid, 0xFF0000FF, "USAGE: /giveweapon [weapon name/id] [Optional: ammo (Default 500)]");
    if(!IsPlayerConnected(targetid) || targetid == INVALID_PLAYER_ID) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Player not connected.");
    if(!(0 < ammo < 999999)) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid Ammo Amount, must be between 1 and 999999.");
    GivePlayerWeapon(targetid, var, ammo);
    new fstr[105], playername[MAX_PLAYER_NAME];
    GetPlayerName(targetid, playername, sizeof(playername));
    GetWeaponName(var, weapname, sizeof(weapname));
    format(fstr, sizeof(fstr), "You gave %s(%d) a %s (ID: %d) with %d ammo.", playername, targetid, weapname, var, ammo);
    SendClientMessage(playerid, 0xFFFF00FF, fstr);
    format(fstr, sizeof(fstr), "You were given a %s with %d ammo.", weapname, ammo);
    SendClientMessage(targetid, 0xFFFF00FF, fstr);
    return 1;
}
--

Your finished code will be:
pawn Code:
new VehicleNames[212][] =
{
    "Landstalker", "Bravura", "Buffalo", "Linerunner", "Pereniel", "Sentinel", "Dumper", "Firetruck", "Trashmaster", "Stretch", "Manana", "Infernus",
    "Voodoo", "Pony", "Mule", "Cheetah", "Ambulance", "Leviathan", "Moonbeam", "Esperanto", "Taxi", "Washington", "Bobcat", "Mr Whoopee", "BF Injection",
    "Hunter", "Premier", "Enforcer", "Securicar", "Banshee", "Predator", "Bus", "Rhino", "Barracks", "Hotknife", "Trailer", "Previon", "Coach", "Cabbie",
    "Stallion", "Rumpo", "RC Bandit", "Romero", "Packer", "Monster", "Admiral", "Squalo", "Seasparrow", "Pizzaboy", "Tram", "Trailer", "Turismo", "Speeder",
    "Reefer", "Tropic", "Flatbed", "Yankee", "Caddy", "Solair", "Berkley's RC Van", "Skimmer", "PCJ-600", "Faggio", "Freeway", "RC Baron", "RC Raider",
    "Glendale", "Oceanic", "Sanchez", "Sparrow", "Patriot", "Quad", "Coastguard", "Dinghy", "Hermes", "Sabre", "Rustler", "ZR3 50", "Walton", "Regina",
    "Comet", "BMX", "Burrito", "Camper", "Marquis", "Baggage", "Dozer", "Maverick", "News Chopper", "Rancher", "FBI Rancher", "Virgo", "Greenwood",
    "Jetmax", "Hotring", "Sandking", "Blista Compact", "Police Maverick", "Boxville", "Benson", "Mesa", "RC Goblin", "Hotring Racer A", "Hotring Racer B",
    "Bloodring Banger", "Rancher", "Super GT", "Elegant", "Journey", "Bike", "Mountain Bike", "Beagle", "Cropdust", "Stunt", "Tanker", "RoadTrain",
    "Nebula", "Majestic", "Buccaneer", "Shamal", "Hydra", "FCR-900", "NRG-500", "HPV1000", "Cement Truck", "Tow Truck", "Fortune", "Cadrona", "FBI Truck",
    "Willard", "Forklift", "Tractor", "Combine", "Feltzer", "Remington", "Slamvan", "Blade", "Freight", "Streak", "Vortex", "Vincent", "Bullet", "Clover",
    "Sadler", "Firetruck", "Hustler", "Intruder", "Primo", "Cargobob", "Tampa", "Sunrise", "Merit", "Utility", "Nevada", "Yosemite", "Windsor", "Monster A",
    "Monster B", "Uranus", "Jester", "Sultan", "Stratum", "Elegy", "Raindance", "RC Tiger", "Flash", "Tahoma", "Savanna", "Bandito", "Freight", "Trailer",
    "Kart", "Mower", "Duneride", "Sweeper", "Broadway", "Tornado", "AT-400", "DFT-30", "Huntley", "Stafford", "BF-400", "Newsvan", "Tug", "Trailer A", "Emperor",
    "Wayfarer", "Euros", "Hotdog", "Club", "Trailer B", "Trailer C", "Andromada", "Dodo", "RC Cam", "Launch", "Police Car (LSPD)", "Police Car (SFPD)",
    "Police Car (LVPD)", "Police Ranger", "Picador", "S.W.A.T. Van", "Alpha", "Phoenix", "Glendale", "Sadler", "Luggage Trailer A", "Luggage Trailer B",
    "Stair Trailer", "Boxville", "Farm Plow", "Utility Trailer"
};

new GunNames[47][] =
{
    "Fist", "Brass Knuckles", "Golf Club", "Nightstick", "Knife", "Baseball Bat",
    "Shovel", "Pool Cue", "Katana", "Chainsaw", "Purple Dildo", "Dildo", "Vibrator",
    "Silver Vibrator", "Flowers", "Cane", "Grenade", "Tear Gas", "Molotov Cocktail",
    "null", "null", "null", "9mm", "Silenced 9mm", "Desert Eagle", "Shotgun", "Sawnoff Shotgun",
    "Combat Shotgun", "Micro SMG/Uzi", "MP5", "AK-47/AK47", "M4", "Tec-9/Tec9/Tec 9", "Country Rifle",
    "Sniper Rifle", "RPG/Rocket Launcher", "Heatseeker/Heat Seeker", "Flamethrower", "Minigun", "Satchel Charges",
    "Detonator", "Spraycan", "Fire Extinguisher", "Camera", "Night Vision Goggles", "Thermal Goggles",
    "Parachute"
};

stock GetVehicleModelFromName(vehname[])
{
    for(new i = 0; i < sizeof(VehicleNames); i++)
    {
        if(strfind(VehicleNames[i], vehname, true) != -1) return i + 400;
    }
    return -1;
}

stock GetWeaponIDFromName(weapname[])
{
    for(new i = 0; i < sizeof(GunNames); i++)
    {
        if(18 < i < 22) continue;
        if(strfind(GunNames[i], weapname, true) != -1) return i;
    }
    return -1;
}

CMD:car(playerid, params[])
{
    new var, vehname[20], col1, col2;
    if(!sscanf(params, "iI(-1)I(-1)", var, col1, col2)) //Player entered a number.
    {
        if(!(400 <= var <= 611)) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid Vehicle Model, must be between 400 and 611.");
    }
    else if(!sscanf(params, "s[20]I(-1)I(-1)", vehname, col1, col2)) //Player entered a string
    {
        var = GetVehicleModelFromName(vehname);
        if(var == -1) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid vehicle name entered.");
    }
    else return SendClientMessage(playerid, 0xFF0000FF, "USAGE: /car [vehicle name/id] [Optional: color1] [Optional: color2]");
    if(col1 < 0) col1 = random(255);
    if(col2 < 0) col2 = random(255);
    new Float:X, Float:Y, Float:Z, Float:A, fstr[58];
    GetPlayerPos(playerid, X, Y, Z);
    GetPlayerFacingAngle(playerid, A);
    PutPlayerInVehicle(playerid, CreateVehicle(var, X, Y, Z + 1.0, A, col1, col2, -1), 0);
    format(fstr, sizeof(fstr), "You successfully created a %s (ID: %d)", VehicleNames[(var - 400)], var);
    SendClientMessage(playerid, 0xFFFF00FF, fstr);
    return 1;
}

CMD:giveweapon(playerid, params[])
{
    new targetid, var, weapname[32], ammo;
    if(!sscanf(params, "uiI(500)", targetid, var, ammo)) //Player entered a number.
    {
        if(!(0 <= var <= 46) || (18 < var < 22)) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid Weapon ID, must be between 0 and 46.");
    }
    else if(!sscanf(params, "is[32]I(500)", targetid, weapname, ammo)) //Player entered a string.
    {
        var = GetWeaponIDFromName(weapname);
        if(var == -1) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid weapon name entered.");
    }
    else return SendClientMessage(playerid, 0xFF0000FF, "USAGE: /giveweapon [weapon name/id] [Optional: ammo (Default 500)]");
    if(!IsPlayerConnected(targetid) || targetid == INVALID_PLAYER_ID) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Player not connected.");
    if(!(0 < ammo < 999999)) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid Ammo Amount, must be between 1 and 999999.");
    GivePlayerWeapon(targetid, var, ammo);
    new fstr[105], playername[MAX_PLAYER_NAME];
    GetPlayerName(targetid, playername, sizeof(playername));
    GetWeaponName(var, weapname, sizeof(weapname));
    format(fstr, sizeof(fstr), "You gave %s(%d) a %s (ID: %d) with %d ammo.", playername, targetid, weapname, var, ammo);
    SendClientMessage(playerid, 0xFFFF00FF, fstr);
    format(fstr, sizeof(fstr), "You were given a %s with %d ammo.", weapname, ammo);
    SendClientMessage(targetid, 0xFFFF00FF, fstr);
    return 1;
}
I scripted this code with 'ZCMD' because that's my personal preference, but the actual commands are not dependent on the processor that you use. You can use ZCMD, YCMD, DCMD etc., but it won't matter in the long run. As long as you are using SSCANF in particular, things will run fairly smoothly.
Reply
#3

Not going to go into much depth, but if you're considering zcmd, just roll with y_commands with an almost identical structure and more features. Plus it's even faster.
Reply
#4

Thank you very much, I really appreciate the throughout answer and that you took the time to write all this info. Thank you for the sscanf part too, would never have managed that on my own, and now I know it for the future too.

Quote:
Originally Posted by nmader
View Post
Not going to go into much depth, but if you're considering zcmd, just roll with y_commands with an almost identical structure and more features. Plus it's even faster.
I'm using y_commands.. although I like zcmd too. Both are really fast.
Reply
#5

Quote:
Originally Posted by Y_Less
View Post
Rather than using "s" for weapon names, I suggest you look in to the "k" specifier. It allows you to write a custom "weapon" specifier similar to what you have already, but supported directly by the plugin.
I know that there is already k<weapon> implemented, but sscanf is hard for me to understand since I'm not that much of an advanced scripter.

How would I go about in doing this?
Reply
#6

Quote:
Originally Posted by Y_Less
View Post
pawn Code:
if (sscanf(params, "k<vehicle>I(-1)I(-1)", var, col1, col2)) return SendClientMessage(playerid, 0xFF0000FF, "USAGE: /car [vehicle name/id] [Optional: color1] [Optional: color2]");
if(var == -1) return SendClientMessage(playerid, 0xFF0000FF, "ERROR: Invalid vehicle name entered.");
Using this puts me in the situation where I have to write the full vehicle names like /car Infernus 3 instead of /car Inf 3, and some vehicles can't be spawned because their names consists of more than one word

And now that I test out the /giveweapon command in game, I'm not able to do "/giveweapon Hannes Sawnoff" to give me a Sawn-off Shotgun, I either have to use "/giveweapon 0 Sawnoff" or "/giveweapon Hannes 26", it seems like it can't handle the player name and weapon string at the same time
Reply
#7

Quote:
Originally Posted by Y_Less
View Post
Sorry, that's a name clash. The original "k<vehicle>" had that problem, I posted a new one but gave it the same name so you would probably still get the old behaviour. Rename that function I posted to "SSCANF:veh2" and use "k<veh2>" (or something else, as long as they match).
No problem, it works perfect now, thanks

Quote:
Originally Posted by [NWA]Hannes
View Post
And now that I test out the /giveweapon command in game ..> it seems like it can't handle the player name and weapon string at the same time
Fixed this so both work, thanks to both of you!
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)