How to: Implement Roleplay Chats


Getting Started
This tutorial will show you step by step on how to implement roleplay chats for your server.
This tutorial also assumes you have general knowledge on how to make commands using zcmd and sscanf 2.6.
A couple of things you need.
Basic understanding in PAWN for sa-mp scripting.
foreach include by Y_Less: http://forum.sa-mp.com/showthread.php?t=92679
sscanf 2.6 plugin by Y_Less: http://forum.sa-mp.com/showthread.php?t=120356
zcmd include by Zeex: http://forum.sa-mp.com/showthread.php?t=91354
Stocks

Before we begin, we will also need a couple of useful functions that will make scripting tons easier.
Note: Stock and public functions are alike in many ways, however the main difference between the two is that stocks may not be called via SetTimer, CallRemoteFunction, etc...
Removing "_" from a name.
In order to do so, we need a string replace function.
Code:
stock strreplace(string[], find, replace)
{
for(new i=0; string[i]; i++)
{
if(string[i] == find)
{
string[i] = replace;
}
}
}
Here is an example of how the string replace stock would replace an underscore from a name.
Code:
new name[24];
GetPlayerName(playerid, name, sizeof(name));
strreplace(name, '_', ' ');
Confused? I'll break it down for you.

Code:
new
name[24];
This just simply defines a string called 'name' with the size of 24. (24 is the max string size of a player name in SA:MP)
Code:
GetPlayerName(playerid, name, sizeof(name));
Using the native function GetPlayerName, we grab the 'name' string, get the name of the player, and store it into the string in the second parameter.
Code:
strreplace(name, '_', ' ');
This is the string replace function in use. The first parameter gets the string. The second one finds what you want to find in the string (which in this case it's the underscore). The last one replaces the characters in the second parameter with the one in the third. So if we want to remove the underscore, we first search for it, and replace it with an empty(null) character (a space).

So now that we have that, we will make another stock called GetName that will be used later in the tutoral.
Code:
stock GetName(playerid)
{
new
name[24];
GetPlayerName(playerid, name, sizeof(name));
strreplace(name, '_', ' ');
return name;
}
ProxDetector
For those who don't know, the famous ProxDetector function was introduced in The Godfather Roleplay. Every time a player would type something into the main chat, it would send the message to the nearby players only, and according to their distance from a player, the message would appear darker or lighter. For instance, if I was 30 feet from a player and they typed in the normal chat, the message would come to me dark. If I were to move closer, the message would become lighter. This function was responsible for making the server chat 'realistic' by sending the message to only the players nearby.
Note: Using the easier-to-understand version, the changing in color in light of the player distance will not be featured.
Code:
stock ProxDetector(Float:radi, playerid, string[],color)
{
new Float:x,Float:y,Float:z;
GetPlayerPos(playerid,x,y,z);
foreach(Player,i)
{
if(IsPlayerInRangeOfPoint(i,radi,x,y,z))
{
SendClientMessage(i,color,string);
}
}
}
Still confused? I'll break it down for you.

Code:
new Float:x,Float:y,Float:z;
This line defines floats that will hold the position of the player. It is important you use the "Float:" tag, because coordinates are decimal numbers.
Code:
GetPlayerPos(playerid, x, y, z);
Using the native function GetPlayerPos we grab the floats defined above, get the player's position, and store the coordinates into the floats.
Code:
foreach(Player, i)
Using foreach, we will efficiently loop through all the players and store the playerids into the variable " i " which is in the second parameter.
Code:
if(IsPlayerInRangeOfPoint(i,radi,x,y,z))
With the foreach loop in the line above, we will now check for every player that is 'in range' of the desired player by using the native function IsPlayerInRangeOfPoint. The " i " in the first parameter represents every player that is in range of the desired player. The second parameter is the range the player has to be in. So if you put 2 in this parameter, then the player has to be within two GTA units of the desired player in order for the statement to be true. Now remember those three floats x, y, and z we defined earlier? This is where it comes in. The last three parameters are the coordinates the player has to be in range of, in this case they have to be in range of the desired player.
Code:
SendClientMessage(i,color,string);
Finally, with all the code above, we send the message to every player who is in range using SendClientMessage. The variable " i " represents all the players in range.

The Local Chat
Now that we have all of our stocks, we will make what's called the 'local' chat. To do this, we will be using the callback called OnPlayerText since it is called every time a person types into the main chat. So go to your script and find "OnPlayerText".
New Message
In order to make the chat realistic, we must format the text so that it is in 'roleplay' format, so we must define a new string that will be the message with a cell size of 128.
Code:
new
message[128];
Formatting the Message

Now that we have defined the the string that will hold our new message, we will use the function format to insert variables like name and text in it.
Code:
format(message, sizeof(message), "%s says: %s", GetName(playerid), text);
Remember the stock GetName from earlier? Well formatting GetName into our message will return the name of whoever typed the message, but it will also remove the underscore! When we insert the "text" into our message, that is the text of whoever typed it. In conclusion, the string will output this.
Code:
NameHere says: Text here.
Sending Message to Players In Range
Now that we have inserted the name and the text, we will now need to send the message to every player in range. To do this, we will now put the ProxDetector function into use!
Code:
ProxDetector(30.0, playerid, message, -1);
The first number you see is "30.0". This means that every player within 30 GTA units of the player will recieve the message. The next parameter is the playerid of whom has typed. Now remember our formatted string message? Well the next parameter is what to send to all the players nearby, which is the formatted message! The last number is the color parameter in which I put "-1". -1 is white, so it'll send the message to everyone nearby in white color. You can also change the color to whatever you want.
Final Step
Up to this point, we have made a new string, inserted the text and name of the player (with underscore removed), and have successfully sent the message to all the players nearby. Everything is working, but one thing. If you have compiled your script up to this point, and go in-game, you will not only see our formatted message, but you will also see the 'traditional' SAMP chat (message sent to all). To get rid of the default SAMP chat, go to the end of your OnPlayerText callback, and instead of
Code:
return 1;
change it to
Code:
return 0;
Returning 0 will get rid of the default SA-MP chat, and send your formatted roleplay message instead.
Conclusion
So after everything we have added under OnPlayerText, this is what is should look like.
Code:
public OnPlayerText(playerid, text[])
{
new
message[128];
format(message, sizeof(message), "%s says: %s", GetName(playerid), text);
ProxDetector(30.0, playerid, message, -1);
return 0;
}
Commands
Before we start, we will need to define the RP purple color on the top of your script.
Code:
#define COLOR_PURPLE 0xC2A2DAAA
So now we have the local chat, but what about the other roleplay chat commands? We will now use the ProxDetector function in a command!
/me
We will be using zcmd, and sscanf 2.6 to create our /me command.
Code:
new
string[128],
action[100];
We will be using 2 different strings. 'string' represents the total message, and 'action' will represent the action the player is doing.
Code:
if(sscanf(params, "s[100]", action))
{
SendClientMessage(playerid, -1, "USAGE: /me [action]");
return 1;
}
Using sscanf 2.6 we will check if the player typed something after typing /me, if they didn't, then sscanf 2.6 will return the message telling them how to use it.
Code:
else
{
format(string, sizeof(string), "* %s %s", GetName(playerid), action);
ProxDetector(30, playerid, string, COLOR_PURPLE);
}
So up to this point, if a player has typed something after /me, we will use an else statement to check if they have. In this else statement we will format the action and the name into the string like we did before! After that has been done, we use our ProxDetector to send it to everyone nearby! Remember the COLOR_PURPLE define? Well COLOR_PURPLE represents the hexadecimal color for purple (classic /me color).
So now we have a /me command and it should look like this. You should now know how to make a /do command!
Code:
CMD:me(playerid, params[])
{
new
string[128],
action[100];
if(sscanf(params, "s[100]", action))
{
SendClientMessage(playerid, -1, "USAGE: /me [action]");
return 1;
}
else
{
format(string, sizeof(string), "* %s %s", GetName(playerid), action);
ProxDetector(30, playerid, string, COLOR_PURPLE);
}
return 1;
}
/shout and /s command
To create a shout command, you would use the same method used above, but what if you want the shout command to be both /shout or /s? I'll show you how.
Code:
CMD:shout(playerid, params[])
{
new
string[128],
shout[100];
if(sscanf(params, "s[100]", shout))
{
SendClientMessage(playerid, -1, "USAGE: /(s)hout [message]");
return 1;
}
else
{
format(string, sizeof(string), "%s shouts: %s!",GetName(playerid),shout);
ProxDetector(50.0, playerid, string, -1);
}
return 1;
}
So that is the shout command, I will not explain it as thoroughly, but you can see we used the same method, only difference is it's in a command now! So if they type /shout it will process the command above, but we also want the command /s to available too!
Code:
CMD:s(playerid, params[]) return cmd_shout(playerid, params);
So, if they type /s, then it will process the actual shout command! If you have learned anything, now you can go on to make a /low command, or a local ooc chat command! Challenge yourself!
Credits
Y_Less for his foreach include, and his sscanf plugin.
Zeex for his zcmd include.
Original creator of the ProxDetector function (used as a reference).
VincentDunn for writing the tutorial.