05.07.2012, 20:58
Some questions that could be discussed in this thread:
When should we use a function?
How should we use a function?
What parameters should the function have? Which should be optional?
When should a function be stock?
When should a function be public?
My views:
Functions (also called 'subroutines') are useful when you want to execute same code many times, only with some small differences each time. One way to solve this problem would be using copy-paste and then change the parts that need to be changed. Another (better) method is to construct a function.
The famous Godfather edits are good examples of scripts that don't use functions very much (or use them inefficiently).
Example taken from a Godfather edit /family command (also known as /f and /faction):
Similar pattern continues until 'member' reaches 16.
It does work, but is this the best way to do it? As you can see, the lines hardly differ. Only things that change are rank id and rank name.
And there are other commands that need to display the same ranks. What if we want to change the name of a rank? We would have to find ALL the places in the script where it occurs and change them.
'format' is used 16 times, 'SendFamilyMessage' 2 times. There must be a way to make it work with only 1 'format' and only 1 'SendFamilyMessage'. This is the point where a function should be made which decides what rank name should be displayed.
Function planning:
1) the function of the function (what should the function do?)
It should return a string with the rank name of a family
2) the name of the function
It should be descriptive enough, so you can get the main idea of what the function does just by looking at the name of it. Let's call ours 'GetFamilyRankName'
3) parameters (what should be the input?)
Rank name depends on family id and rank id, so we need to input them as parameters
4) writing the function
After creating the GetFamilyRankName function, our example will turn into this
with only 1 format and 1 SendFamilyMessage used.
Now, if you want to change a rank or add a rank, all you have to do is make the needed changes in GetFamilyRankName and that's it! You don't need to even touch the commands that use rank names.
***
If you are making a function that does something with a string that is passed as a parameter, you might need to know the size of that string. For example when using strins in it. Compiling something like this
will give
because the last parameter of strins (maxlength) is by default 'sizeof string'. But compiler doesn't know the size of 'abc' at this point. To fix this problem, you can use the same method that strins uses: silently pass the string size as an optional parameter
Then you can still call the function like this
without needing to worry about the size of the string.
***
When should a function be stock?
I add 'stock' in front of functions that are independent. By that I mean they can be used in any other script without the need to modify them
Examples of stocks
When should a function be public?
Only when it's called ...
1) ... by a timer (SetTimer/SetTimerEx)
2) ... from another script (CallRemoteFunction)
3) ... from the server (callbacks)
If the function doesn't fall into one of these categories, it doesn't need to be public.
This section of the forums is for discussing and so is this thread. You can add new points, explain your own views/scripting habits, add new questions to be discussed - anything related to (PAWN) functions.
When should we use a function?
How should we use a function?
What parameters should the function have? Which should be optional?
When should a function be stock?
When should a function be public?
My views:
Functions (also called 'subroutines') are useful when you want to execute same code many times, only with some small differences each time. One way to solve this problem would be using copy-paste and then change the parts that need to be changed. Another (better) method is to construct a function.
The famous Godfather edits are good examples of scripts that don't use functions very much (or use them inefficiently).
Example taken from a Godfather edit /family command (also known as /f and /faction):
pawn Code:
new leader = PlayerInfo[playerid][pLeader];
new member = PlayerInfo[playerid][pMember];
if(member==1)
{
if(PlayerInfo[playerid][pRank] == 8) { format(string, sizeof(string), "** (( Chief %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 7) { format(string, sizeof(string), "** (( Deputy Chief %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 6) { format(string, sizeof(string), "** (( Captain %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 5) { format(string, sizeof(string), "** (( Lieutenant %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 4) { format(string, sizeof(string), "** (( Sergeant %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 3) { format(string, sizeof(string), "** (( Corporal %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 2) { format(string, sizeof(string), "** (( Police Officer %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 1) { format(string, sizeof(string), "** (( Cadet %s: %s )) **", sendername, result); }
else { format(string, sizeof(string), "** (( Cadet %s: %s )) **", sendername, result); }
SendFamilyMessage(PlayerInfo[playerid][pMember], 0x7BDDA5AA, string);
}
if(member==2)
{
if(PlayerInfo[playerid][pRank] == 6) { format(string, sizeof(string), "** (( Director %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 5) { format(string, sizeof(string), "** (( Assistant Director in Charge %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 4) { format(string, sizeof(string), "** (( Special Agent in Charge %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 3) { format(string, sizeof(string), "** (( Special Agent %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 2) { format(string, sizeof(string), "** (( Special Agent Trainee %s: %s )) **", sendername, result); }
else if(PlayerInfo[playerid][pRank] == 1) { format(string, sizeof(string), "** (( Professional Staff %s: %s )) **", sendername, result); }
else { format(string, sizeof(string), "** (( Professional Staff %s: %s )) **", sendername, result); }
SendFamilyMessage(PlayerInfo[playerid][pMember], 0x7BDDA5AA, string);
}
It does work, but is this the best way to do it? As you can see, the lines hardly differ. Only things that change are rank id and rank name.
And there are other commands that need to display the same ranks. What if we want to change the name of a rank? We would have to find ALL the places in the script where it occurs and change them.
'format' is used 16 times, 'SendFamilyMessage' 2 times. There must be a way to make it work with only 1 'format' and only 1 'SendFamilyMessage'. This is the point where a function should be made which decides what rank name should be displayed.
Function planning:
1) the function of the function (what should the function do?)
It should return a string with the rank name of a family
2) the name of the function
It should be descriptive enough, so you can get the main idea of what the function does just by looking at the name of it. Let's call ours 'GetFamilyRankName'
3) parameters (what should be the input?)
Rank name depends on family id and rank id, so we need to input them as parameters
pawn Code:
GetFamilyRankName(familyid, rankid)
pawn Code:
GetFamilyRankName(familyid, rankid)
{
new name[32];
switch(familyid)
{
case 1:
{
switch(rankid)
{
case 1: name = "Cadet";
case 2: name = "Police Officer";
case 3: name = "Corporal";
case 4: name = "Sergeant";
case 5: name = "Lieutenant";
case 6: name = "Captain";
case 7: name = "Deputy Chief";
case 8: name = "Chief";
default: name = "Cadet";
}
}
case 2:
{
switch(rankid)
{
case 1: name = "Professional Staff";
case 2: name = "Special Agent Trainee";
case 3: name = "Special Agent";
case 4: name = "Special Agent in Charge";
case 5: name = "Assistant Director in Charge";
case 6: name = "Director";
default: name = "Professional Staff";
}
}
}
return name;
}
pawn Code:
new member = PlayerInfo[playerid][pMember];
new rank = PlayerInfo[playerid][pRank];
format(string, sizeof(string), "** (( %s %s: %s )) **", GetFamilyRankName(member, rank), sendername, result);
SendFamilyMessage(member, 0x7BDDA5AA, string);
Now, if you want to change a rank or add a rank, all you have to do is make the needed changes in GetFamilyRankName and that's it! You don't need to even touch the commands that use rank names.
***
If you are making a function that does something with a string that is passed as a parameter, you might need to know the size of that string. For example when using strins in it. Compiling something like this
pawn Code:
InsertSomething(abc[], pos)
{
strins(abc, "insert this", pos);
}
Code:
warning 224: indeterminate array size in "sizeof" expression (symbol "maxlength")
pawn Code:
InsertSomething(abc[], pos, maxlength=sizeof abc)
{
strins(abc, "insert this", pos, maxlength);
}
pawn Code:
InsertSomething(text, 5);
***
When should a function be stock?
I add 'stock' in front of functions that are independent. By that I mean they can be used in any other script without the need to modify them
Examples of stocks
pawn Code:
stock IsPlayerSpawned(playerid)
{
switch(GetPlayerState(playerid))
{
case 1,2,3: return 1;
}
return 0;
}
pawn Code:
stock GetVehicleDriver(vehicleid)
{
for(new i=0; i < MAX_PLAYERS; i++)
{
if(IsPlayerConnected(i) && GetPlayerVehicleID(i) == vehicleid)
{
if(GetPlayerState(i) == PLAYER_STATE_DRIVER)
{
return i;
}
}
}
return INVALID_PLAYER_ID;
}
When should a function be public?
Only when it's called ...
1) ... by a timer (SetTimer/SetTimerEx)
2) ... from another script (CallRemoteFunction)
3) ... from the server (callbacks)
If the function doesn't fall into one of these categories, it doesn't need to be public.
This section of the forums is for discussing and so is this thread. You can add new points, explain your own views/scripting habits, add new questions to be discussed - anything related to (PAWN) functions.