03.05.2015, 23:26
(
Последний раз редактировалось Ahmad45123; 07.05.2015 в 14:53.
)
This is a FAQ which I will be adding most asked questions, I added some which ****** have written them a while ago and adding more soon.
Suggestions are welcome in replies about the popular questions.
____________________________
Here we have one command, "/heal" that we want to convert. y_commands doesn't use "OnPlayerCommandText",
so we can delete all of that outside our command:
The first line is now the part that compares what the user typed (at least the first part of what
they typed) to one of the possible commands. We need to change that to y_commands syntax:
The command is called "heal", and all y_commands commands start with "CMD:" and take two parameters after
the command name. This gives us:
This now gives us:
We are almost there, but this won't yet compile because of lines like this:
Really, you should be using "sscanf" to handle parameters, but for simplicity we will convert this
as well. The problem is that we have deleted the code in which "index" is declared and no longer
have "cmdtext" either. Solve this by declaring a new variable called "index" and changing "cmdtext"
to "params":
NOTE: If you are going to convert from strcmp to ZCMD, I highly recommend changing to sscanf too.
support this tool, which looks for text patterns and replaces those patterns with other patterns of
text. This isn't a tutorial on RegEx itself, nor on those editors (both of which are well
documented elsewhere) but you can use this search/replace system:
Search Pattern:
Replace Pattern:
That will replace any of these styles of command headers:
Also more spacious options. It will NOT replace:
The "/" is explicitly searched for.
You still need to delete the remainder of "OnPlayerCommandText" yourself, and add in any newly
required "index" variables yourself. Changing "cmdtext" to "params", though, is a simple search and
replace job.
functions. The first step is simple - delete "OnPlayerCommandText", all of it. No demo necessary,
just highlight the function and press "delete".
Now we need to go from this:
To this:
This is not hard at all. You can either manually replace "dcmd_" with "CMD:", or you can do a
global search and replace to have the computer do it for you. Just be careful that there aren't any
instances of that text that you don't want to be replaced.
convert at all. This is a command in "y_commands":
This is a command in "ZCMD":
"y_commands" can have a third "help" parameter, but this is OPTIONAL. If you want to use this you
need to change "CMD:" to "YCMD:" and add in the third parameter:
You should also probably USE the "help" parameter:
The body of the "else" branch is just all of the old code. The body of the "then" branch is
instructions on what the command does and how to use it, i.e. "help" on the command.
"strcmp" with "sscanf", or "strcat" with "ZCMD" (as shown in some other questions). The reason that
"sscanf" is often grouped with "ZCMD" is that they are both good techniques and produce better
commands than any using "strcmp" or "strtok". But because they are separate, converting the parts
is done separately as well.
Let us start with a command. This is a "/givecash" command, but includes an optional reason of WHY
you are sending that player money (to demonstrate a range of parameter types). It is written in
ZCMD, but could have just been converted to that from "strcmp":
Apologies for the length of this command, but that is because of the use of "strtok". There is no
automatic way to convert to "sscanf", and the best way to learn what to do is by reading the
documentation, but we will try and explain this conversion process.
There are three parameters in the above command, each represented by a different letter in "sscanf"
- a target player (playerid, "u"), an amount (integer, "i"), and a reason (optional string,
"S[length]"). "sscanf" takes a data source, a description of how the data in that source is
formatted, and variables - one for each piece of data to be stored in:
It also returns 0 (zero) when the data is correctly extracted, and NOT 0 when the data isn't
correctly extracted (i.e. when the wrong data is given):
Because we are no longer using "strtok" we no longer need "index" or "tmp" so they can be deleted.
We also don't need the "strtok" lines, or the lines checking if "tmp" is empty and if not converting
the data to numbers. You do, however, need to check that the parameters are valid for your uses.
For example, "i" returns an integer, so if a user types "12345" then "sscanf" is happy and will pass
- but the player may not have that much money:
There are still some improvements to make.
That is great, and you decide to show the player their data:
Works perfectly, giving the expected output:
Then you decide to show them EVERY player's data:
Expected output:
Actual output:
Have fun figuring out WHY! In short: don't use global strings!
We don't know!
There is a lot of free tutorials on internet, Just ******.
After that follow this great tutorial here: https://sampforum.blast.hk/showthread.php?tid=485633
It will help you setup MySQL on your computer and create your first MySQL script which will teach you how to use MySQL in PAWN. (Assuming you already know MySQL language now).
Then, You should be ready to go and convert your gamemode.
To be honest, There isn't a certain syntax to be used.. Just delete the whole current saving/loading system(Always backup first) then recode it the same way you learnt in the previous tutorial.
Suggestions are welcome in replies about the popular questions.
____________________________
How do I convert strcmp to y_commands?
The simplest way to explain this is with an example:pawn Код:
public OnPlayerCommandText(playerid, cmdtext[])
{
new
index,
cmd[32];
cmd = strtok(cmdtext, index);
if (strcmp(cmd, "/heal", true) == 0)
{
new
tmp[32];
tmp = strtok(cmdtext, index);
if (!strlen(tmp)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /heal <ID>");
if (!IsPlayerConnected(strval(tmp))) return SendClientMessage(playerid, 0xFF0000AA, "Player not found");
SetPlayerHealth(strval(tmp), 100.0);
SendClientMessage(strval(tmp), 0x00FF00AA, "You have been healed");
SendClientMessage(playerid, 0x00FF00AA, "Player healed");
return 1;
}
// OTHER COMMANDS.
return 0;
}
so we can delete all of that outside our command:
pawn Код:
if (strcmp(cmd, "/heal", true) == 0)
{
new
tmp[32];
tmp = strtok(cmdtext, index);
// COMMAND BODY.
return 1;
}
// OTHER COMMANDS.
they typed) to one of the possible commands. We need to change that to y_commands syntax:
pawn Код:
if (strcmp(cmd, "/heal", true) == 0)
the command name. This gives us:
pawn Код:
CMD:heal(playerid, params[])
pawn Код:
CMD:heal(playerid, params[])
{
new tmp[32];
tmp = strtok(cmdtext, index);
// COMMAND BODY.
return 1;
}
// OTHER COMMANDS.
pawn Код:
tmp = strtok(cmdtext, index);
as well. The problem is that we have deleted the code in which "index" is declared and no longer
have "cmdtext" either. Solve this by declaring a new variable called "index" and changing "cmdtext"
to "params":
pawn Код:
CMD:heal(playerid, params[])
{
new tmp[32];
new index; // New variable.
tmp = strtok(params, index); // Changed variable.
// COMMAND BODY.
return 1;
}
// OTHER COMMANDS.
Is there a way to automate this?
Yes, if your editor supports "Regular Expressions" (aka "RegEx"). Both Sublime Text and Notepad++support this tool, which looks for text patterns and replaces those patterns with other patterns of
text. This isn't a tutorial on RegEx itself, nor on those editors (both of which are well
documented elsewhere) but you can use this search/replace system:
Search Pattern:
Код:
if[ (!]*strcmp[ (]*cmd.*/(.*)".*
Код:
CMD:\1\(playerid, params[]\)
pawn Код:
if (strcmp(cmd, "/heal", true) == 0)
if (!strcmp (cmd, "/heal", true) == 0)
if ( strcmp( cmdtext, "/heal", true, 5 ) == 0 )
if ( !strcmp(cmdtext, "/heal", true, 5) )
pawn Код:
if (strcmp(cmd[1], "heal", true) == 0)
You still need to delete the remainder of "OnPlayerCommandText" yourself, and add in any newly
required "index" variables yourself. Changing "cmdtext" to "params", though, is a simple search and
replace job.
How do I convert DCMD to y_commands?
This is much simpler than converting "strcmp" to "y_commands" because all the commands are already infunctions. The first step is simple - delete "OnPlayerCommandText", all of it. No demo necessary,
just highlight the function and press "delete".
Now we need to go from this:
pawn Код:
dcmd_heal(playerid, params[])
pawn Код:
CMD:heal(playerid, params[])
global search and replace to have the computer do it for you. Just be careful that there aren't any
instances of that text that you don't want to be replaced.
How do I convert y_commands to ZCMD?
"ZCMD" and "y_commands" both use the same syntax for declaring commands, so there is no need toconvert at all. This is a command in "y_commands":
pawn Код:
CMD:heal(playerid, params[])
pawn Код:
CMD:heal(playerid, params[])
need to change "CMD:" to "YCMD:" and add in the third parameter:
pawn Код:
YCMD:heal(playerid, params[], help)
pawn Код:
YCMD:heal(playerid, params[], help)
{
if (help)
{
return SendClientMessage(playerid, 0xFF0000AA, "Heals a player. Usage : /heal <ID>");
}
else
{
// Do the actual command.
return 1;
}
}
instructions on what the command does and how to use it, i.e. "help" on the command.
How do I convert strtok to sscanf?
Most people think of "sscanf" as part of "ZCMD" - it isn't. There is no reason why you can't use"strcmp" with "sscanf", or "strcat" with "ZCMD" (as shown in some other questions). The reason that
"sscanf" is often grouped with "ZCMD" is that they are both good techniques and produce better
commands than any using "strcmp" or "strtok". But because they are separate, converting the parts
is done separately as well.
Let us start with a command. This is a "/givecash" command, but includes an optional reason of WHY
you are sending that player money (to demonstrate a range of parameter types). It is written in
ZCMD, but could have just been converted to that from "strcmp":
pawn Код:
CMD:givecash(playerid, params[])
{
new
tmp[32],
index;
// Get the player to give money to.
tmp = strtok(params, index);
if (!strlen(tmp)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /givecash <ID> <Amount> [Optional reason]");
new
giveplayerid = strval(tmp);
if (!IsPlayerConnected(giveplayerid)) return SendClientMessage(playerid, 0xFF0000AA, "Player not found.");
// Get the amount of money to give.
tmp = strtok(params, index);
if (!strlen(tmp)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /givecash <ID> <Amount> [Optional reason]");
new
amount = strval(tmp);
if (amount < 0) return SendClientMessage(playerid, 0xFF0000AA, "You must enter a positive amount of money.");
else if (amount >= GetPlayerMoney(playerid)) return SendClientMessage(playerid, 0xFF0000AA, "You do not have that much money to give.");
// Get the optional message.
new
reason[128];
strcpy(reason, params[index]);
// Do the transfer.
GivePlayerMoney(playerid, -amount);
GivePlayerMoney(giveplayerid, amount);
// Send messages.
new
message[128];
if (strlen(reason))
{
format(message, sizeof (message), "You have sent $%d to %d for reason: %s", amount, giveplayerid, reason);
SendClientMessage(playerid, 0xFF0000AA, message);
format(message, sizeof (message), "%d has sent you $%d for reason: %s", playerid, amount, reason);
SendClientMessage(giveplayerid, 0xFF0000AA, message);
}
else
{
format(message, sizeof (message), "You have sent $%d to %d for no reason", amount, giveplayerid);
SendClientMessage(playerid, 0xFF0000AA, message);
format(message, sizeof (message), "%d has sent you $%d for no reason", playerid, amount);
SendClientMessage(giveplayerid, 0xFF0000AA, message);
}
return 1;
}
automatic way to convert to "sscanf", and the best way to learn what to do is by reading the
documentation, but we will try and explain this conversion process.
There are three parameters in the above command, each represented by a different letter in "sscanf"
- a target player (playerid, "u"), an amount (integer, "i"), and a reason (optional string,
"S[length]"). "sscanf" takes a data source, a description of how the data in that source is
formatted, and variables - one for each piece of data to be stored in:
pawn Код:
new
giveplayerid,
amount,
reason[128];
sscanf(params, "uiS[128]", giveplayerid, amount, reason);
correctly extracted (i.e. when the wrong data is given):
pawn Код:
if (sscanf(params, "uiS[128]", giveplayerid, amount, reason)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /givecash <ID> <Amount> [Optional reason]");
We also don't need the "strtok" lines, or the lines checking if "tmp" is empty and if not converting
the data to numbers. You do, however, need to check that the parameters are valid for your uses.
For example, "i" returns an integer, so if a user types "12345" then "sscanf" is happy and will pass
- but the player may not have that much money:
pawn Код:
// Check the parameters are valid.
if (!IsPlayerConnected(giveplayerid)) return SendClientMessage(playerid, 0xFF0000AA, "Player not found.");
else if (amount < 0) return SendClientMessage(playerid, 0xFF0000AA, "You must enter a positive amount of money.");
else if (amount >= GetPlayerMoney(playerid)) return SendClientMessage(playerid, 0xFF0000AA, "You do not have that much money to give.");
- "sscanf" sets a variable to "INVALID_PLAYER_ID" when you type something that looks like a name or
ID (when using "u"), but they aren't connected. - You don't need to check the length to see if a string is empty, you can just use "isnull".
- "SendClientMessageFormatted" exists in many forms and is very useful.
pawn Код:
CMD:givecash(playerid, params[])
{
// Get all the parameters at once.
new
giveplayerid,
amount,
reason[128];
if (sscanf(params, "uiS[128]", giveplayerid, amount, reason)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /givecash <ID> <Amount> [Optional reason]");
// Check the parameters are valid.
if (giveplayerid == INVALID_PLAYER_ID) return SendClientMessage(playerid, 0xFF0000AA, "Player not found.");
if (amount < 0) return SendClientMessage(playerid, 0xFF0000AA, "You must enter a positive amount of money.");
if (amount >= GetPlayerMoney(playerid)) return SendClientMessage(playerid, 0xFF0000AA, "You do not have that much money to give.");
// Do the transfer.
GivePlayerMoney(playerid, -amount);
GivePlayerMoney(giveplayerid, amount);
// Send messages (we have swapped the branches around).
if (isnull(reason))
{
SendClientMessageFormatted(playerid, 0xFF0000AA, "You have sent $%d to %d for no reason", amount, giveplayerid);
SendClientMessageFormatted(giveplayerid, 0xFF0000AA, "%d has sent you $%d for no reason", playerid, amount);
}
else
{
SendClientMessageFormatted(playerid, 0xFF0000AA, "You have sent $%d to %d for reason: %s", amount, giveplayerid, reason);
SendClientMessageFormatted(giveplayerid, 0xFF0000AA, "%d has sent you $%d for reason: %s", playerid, amount, reason);
}
return 1;
}
Should I use one global string or lots of local ones?
Lots of local ones. Here is why:pawn Код:
new
gString[1024];
GetOnePlayerData(playerid)
{
new
Float:health,
Float:armour;
GetPlayerHealth(playerid, health);
GetPlayerArmour(playerid, armour);
format(gString, sizeof (gString), "Health: %.2f, Armour: %.2f, Score: %d, Cash: $%d",
health,
armour,
GetPlayerScore(playerid),
GetPlayerMoney(playerid));
return gString;
}
pawn Код:
format(gString, sizeof (gString), "Your data: %s", GetOnePlayerData(playerid));
ShowPlayerDialog(playerid, 5050, DIALOG_STYLE_MSGBOX, "Your Data", gString, "OK", "");
Код:
Your data: "Health: 100.00, Armour: 100.00, Score: 53, Cash: $898
pawn Код:
gString[0] = '\0';
foreach (new i : Player)
{
format(gString, sizeof (gString), "%s%d: %s\n", gString, i, GetOnePlayerData(i));
}
ShowPlayerDialog(playerid, 5050, DIALOG_STYLE_MSGBOX, "Player Data", gString, "OK", "");
Код:
1: "Health: 100.00, Armour: 80.00, Score: 2797, Cash: $364 2: "Health: 95.00, Armour: 0.00, Score: 845, Cash: $52 5: "Health: 100.00, Armour: 0.00, Score: 9784, Cash: $2456 6: "Health: 32.00, Armour: 0.00, Score: 678, Cash: $648 8: "Health: 100.00, Armour: 100.00, Score: 53, Cash: $898
Код:
"Health: 100.00, Armour: 100.00, Score: 53, Cash: $8988: "Health: 100.00, Armour: 100.00, Score: 53, Cash: $898
I heard "X" is slow, is it?
NOTE: X means anything here.We don't know!
- Computers run millards (that's billions to you Americans) of instructions every second, they can do A LOT, so even slow things can be done very quickly.
- If your function is only run once an hour, does it even MATTER is it is slow? A function that takes 1 millisecond to execute (and that is VERY slow to a computer), but only runs once every 3600000 milliseconds (that's an hour BTW) is only 0.000027% of your total code being run. Who CARES if it is slow? If you improve that code 1000x so it takes just one microsecond to run, your total code execution time has improved by an insignificant amount too tiny to even measure!
- Before worrying about whether "switch" is faster than "if", just remember that if you find out and change, the total amount time you will save by using the better code is less than the time it took you to read this sentence.
How to convert to MySQL ?
First of all, You should know that SQL is a new language.. So I suggest you learn it first (Its pretty simple, Believe me).There is a lot of free tutorials on internet, Just ******.
After that follow this great tutorial here: https://sampforum.blast.hk/showthread.php?tid=485633
It will help you setup MySQL on your computer and create your first MySQL script which will teach you how to use MySQL in PAWN. (Assuming you already know MySQL language now).
Then, You should be ready to go and convert your gamemode.
To be honest, There isn't a certain syntax to be used.. Just delete the whole current saving/loading system(Always backup first) then recode it the same way you learnt in the previous tutorial.