SA-MP Forums

Go Back   SA-MP Forums > SA-MP Scripting and Plugins > Scripting Help > Tutorials

Reply
 
Thread Tools Display Modes
Old 05/07/2012, 08:58 PM   #1
MadeMan
High-roller
 
MadeMan's Avatar
 
Join Date: Jun 2007
Posts: 3,472
Reputation: 237
Default Functions

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):

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);
}

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

pawn Code:
GetFamilyRankName(familyid, rankid)

4) writing the function

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;
}

After creating the GetFamilyRankName function, our example will turn into this

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);

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

pawn Code:
InsertSomething(abc[], pos)
{
    strins(abc, "insert this", pos);
}

will give

Code:
warning 224: indeterminate array size in "sizeof" expression (symbol "maxlength")
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

pawn Code:
InsertSomething(abc[], pos, maxlength=sizeof abc)
{
    strins(abc, "insert this", pos, maxlength);
}

Then you can still call the function like this

pawn Code:
InsertSomething(text, 5);

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

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.
MadeMan is offline   Reply With Quote
Old 05/07/2012, 09:21 PM   #2
TheChaoz
High-roller
 
TheChaoz's Avatar
 
Join Date: Dec 2009
Location: Argentina
Posts: 4,684
Reputation: 215
Default Respuesta: Functions

A function is not the same as a subrutine. The main difference is that a sub rutine just execute code in a certain order, while a function do stuff with the parameters passed and may return a value, an other big difference between them is that a function should not change values outside the function itself while a subrutine may change some external values.
__________________
TheChaoz is offline   Reply With Quote
Old 05/07/2012, 10:26 PM   #3
Vince
Godfather
 
Vince's Avatar
 
Join Date: Sep 2007
Location: Belgium
Posts: 6,599
Reputation: 1719
Default Re: Functions

"It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. Basic professional ethics would instead require him to write a DestroyCity procedure, to which Baghdad could be given as a parameter." - Nathaniel Borenstein

Now, for the rank example: personally, I use constant arrays for those. Of course they don't need to be constants, but I do not intend on changing these via the script so they might as well be. Now, whenever I want to retrieve the rank, I simply pass the player's value as the index parameter (if that makes sense) without having to resort to large switch statements;

pawn Code:
new const stock ePoliceLevels[11][] = {
    {"Cadet"},                  // 0
    {"Officer In Training"},    // 1
    {"Officer"},                // 2
    {"Senior Officer"},         // 3
    {"Sergeant"},               // 4
    {"Lieutenant"},             // 5
    {"Captain"},                // 6
    {"Inspector"},              // 7
    {"Commandant"},             // 8
    {"Chief"},                  // 9
    {"Commissioner"}            // 10
};

PlayerStats[cop][pLevel]++;

format(cString, sizeof(cString), "  Your rank will be increased to %s.",
    ePoliceLevels[PlayerStats[cop][pLevel]]);
SendClientMessage(cop, COLOR_LIGHTBLUE, cString);

Of course this is not the entire code and additional checks are in place here to guard against OOB errors.
__________________

Vince is offline   Reply With Quote
Old 06/07/2012, 08:51 AM   #4
MadeMan
High-roller
 
MadeMan's Avatar
 
Join Date: Jun 2007
Posts: 3,472
Reputation: 237
Default Re: Functions

Quote:
Originally Posted by the_chaoz View Post
A function is not the same as a subrutine. The main difference is that a sub rutine just execute code in a certain order, while a function do stuff with the parameters passed and may return a value, an other big difference between them is that a function should not change values outside the function itself while a subrutine may change some external values.
Well Wikipedia sees them as one - http://en.wikipedia.org/wiki/Functio...ter_science%29
MadeMan is offline   Reply With Quote
Old 06/07/2012, 12:57 PM   #5
Niko_boy
High-roller
 
Niko_boy's Avatar
 
Join Date: Aug 2010
Location: Somewhere i belong
Posts: 1,416
Reputation: 137
Default Re: Functions

Ty , i hardy able to create differnce b/w a stock function and a simepel function [excluding callbacks]
this helped me clarifying a bit :P
__________________
$$$ If anyone want to get any of these:
  • DM/TDM/Freeroam/Stunt server, filterscripts or Bug fixing.or some general mapping. Above all any of the logos and banners or signature sorta stuff aswell.at some cheap and worth-full prices.
can Contact me for more info or a deal.
•••[0.3x]LCS•Freeroam•DM•Stunts•Race•Parkour•••AutoArena [0.3z][No SkinShot][sixtytiger.com]Want a decent Attack Defend Gamemode?
176.31.120.76:7777176.31.229.148:7830Get This! Attack-Defend(v2.3.1)
Niko_boy is offline   Reply With Quote
Old 06/07/2012, 02:28 PM   #6
Steeve_Smith
Big Clucker
 
Steeve_Smith's Avatar
 
Join Date: Oct 2010
Location: France
Posts: 156
Reputation: 11
Default Re : Functions

When I scripted my own FS, I noticed that this kind of functions are very useful, so even if I already know that, nice job, it will help more than one scripter
__________________


Helped you? me and I will help you more!
Steeve_Smith is offline   Reply With Quote
Old 29/08/2012, 08:09 PM   #7
Guitar
Huge Clucker
 
Guitar's Avatar
 
Join Date: Feb 2012
Location: ►░▒▓♫♪▓▒░◄
Posts: 366
Reputation: 3
Default Re: Functions

Hm, this is going to be useful and helpful for me if I wanted to make the admins show with their ranks, thank you
__________________
3habGamingY_INI Registration system
Click meClick me
Code:
funland.dyndns.org:7777 - Connect and test my work :) [Tutorial] Making a registration system - Using "Y_INI + Whirlpool + Dialogs" 3habGaming is looking for members/helpers/staff - www.3habgaming.icyboards.net :oops:. I am interested in learning the basics to a Roleplay GM.
Jansish is great :) + I hate Dialogs :(
Guitar is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
MySQL R6 functions with R7 functions Tee Scripting Help 1 25/06/2012 03:08 AM
[Include] [INC] LSF - Lorenc's Simple Functions (w/ gang/clan functions) Lorenc_ Includes 11 03/05/2010 09:47 PM
My Functions.inc mini-d Help Archive 6 27/08/2009 06:10 AM
[Include] [INC] Two useful functions Paladin Includes 2 25/08/2009 01:55 PM
What this functions do? harrold Help Archive 2 08/05/2009 11:30 PM


All times are GMT. The time now is 06:54 AM.


Powered by vBulletin® Version 3.8.6
Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.