Useful Functions

GetPlayerCameraLookAt

pawn Код:
stock GetPlayerCameraLookAt(playerid, &Float:X, &Float:Y, &Float:Z)
{
    new Float:pos[6];
    GetPlayerCameraPos(playerid, pos[0], pos[1], pos[2]);
    GetPlayerCameraFrontVector(playerid, pos[3], pos[4], pos[5]);
    X = floatadd(pos[0], pos[3]);
    Y = floatadd(pos[1], pos[4]);
    Z = floatadd(pos[2], pos[5]);
    return 1;
}
Return's X, Y, Z pos.

pawn Код:
new Float:x, Float:y, Float:z;
GetPlayerCameraPos(playerid, x, y, z);
printf("%f, %f, %f", x, y, z);
GetPlayerCameraLookAt(playerid, x, y, z);
printf("%f, %f, %f", x, y, z);
If you found any bug, please report.
Reply

A function that measures the distance from a point to another point, that really works! (in Metters).

PS: not done by me

PSІ: I'm posting because I've taken a long time to find

pawn Код:
stock Float:GetDistanceBetweenPoints(Float:X, Float:Y, Float:Z, Float:PointX, Float:PointY, Float:PointZ)
{
    new Float:Distance;Distance = floatabs(floatsub(X, PointX)) + floatabs(floatsub(Y, PointY)) + floatabs(floatsub(Z, PointZ));
    return Distance;
}
Reply

pawn Код:
ShowPlayerDialogEx(playerid, dialogid, dialogtype, caption[], body[], body_placeholder[], button1[], button2[])
{
    if(!strlen(body))
    {
        return ShowPlayerDialog(playerid, dialogid, dialogtype, caption, body_placeholder, button1, button2);
    }
   
    return ShowPlayerDialog(playerid, dialogid, dialogtype, caption, body, button1, button2);
}
Usage:

pawn Код:
new dialogBody[1024];

for(new playerIndex; playerIndex < MAX_PLAYERS; playerIndex++)
{
    if(IsPlayerAdmin(playerIndex))
    {
        format(dialogBody, sizeof(dialogBody), "%sID %d is an RCON admin\n", dialogBody, playerIndex);
    }
}

ShowPlayerDialogEx(playerid, DIALOG_RCONADMINS, DIALOG_STYLE_LIST, "RCON Administrators", dialogBody, "There are no RCON administrators online.", "Close", "");
This lets you set a placeholder after the dialog body parameter. The placeholder will replace the dialog's body if the body is empty.
Reply

Detect Invalid Color Tags in GameTextForAll/GameTextForPlayer
Edit:
Improved the code to auto fix invalid color tags. (delete invalid tags from the string).

Code:
pawn Код:
stock IsValidTag(text) //The valid colors
{
  switch(text)
  {
       case 'G': return 1;
       case 'P': return 1;
       case 'B': return 1;
       case 'R': return 1;
       case 'Y': return 1;
       case 'H': return 1;
       case 'W': return 1;
       case 'N': return 1;
       case 'g': return 1;
       case 'p': return 1;
       case 'b': return 1;
       case 'r': return 1;
       case 'y': return 1;
       case 'h': return 1;
       case 'w': return 1;
       case 'n': return 1;
  }
  return 0;
}
pawn Код:
stock GametextForAllEx(text[], time, style)
{
    new i = 0, pos;
    while (text[i])
    {
        if((pos = strfind(text[i],"~",true)) != -1)
        {
           if(text[i] == '~' && IsValidTag(text[i+1]) && text[i+2] == '~')
           i = i+2;
           else
           if(text[i] == '~')
           {
           strdel(text[i],pos,pos+1);
           continue;
           }
        }
        i++;
    }
    return GameTextForAll(text, time, style);
}
pawn Код:
stock GameTextForPlayerEx(playerid, text[], time, style)
{
    new i = 0, pos;
    while (text[i])
    {
        if((pos = strfind(text[i],"~",true)) != -1)
        {
           if(text[i] == '~' && IsValidTag(text[i+1]) && text[i+2] == '~')
           i = i+2;
           else
           if(text[i] == '~')
           {
           strdel(text[i],pos,pos+1);
           continue;
           }
        }
        i++;
    }
    return GameTextForPlayer(playerid, text, time, style);
}
Example:

pawn Код:
new string[128] = "~B~Invalid ~N~colo~U~r ~tags ~N~detected";
GametextForAllEx(string,8000,3); //Replace the GametextForAll with GametextForAllEx
Results:

Quote:

~B~Invalid ~N~colour tags ~N~detected

Reply

Some math functions (probably seen way before this day)..

pawn Код:
stock GetOffsetDegrees(Float:a, Float:b)
{
    new Float:c;
    c = (b > a) ? b - a : 0.0 - (a - b);
    if(c > 180.0) c = 0.0 - (360.0 - c);
    else if(c <= -180.0) c = (360.0 + c);
    return c;
}

stock GetDistanceBetweenPoints2D(Float:x, Float:y, Float:xx, Float:yy)
{
    new Float:newx = (xx - x);
    new Float:newy = (yy - y);
    return floatsqroot( (newx * newx) + (newy * newy) );
}

stock GetDistanceBetweenPoints3D(Float:x, Float:y, Float:z, Float:xx, Float:yy, Float:zz)
{
    new Float:newx = (xx - x);
    new Float:newy = (yy - y);
    new Float:newz = (zz - z);
    return floatsqroot( (newx * newx) + (newy * newy) + (newz * newz));
}
Reply

Function to check is some IP address within an IP range. You need to have strexplode.

Код:
stock ip2int(ip_address[16])
{
	new sIP[4][4], IP[4];
	strexplode(sIP, ip_address, !".", _, false, _, 4, 4);

	for(new i = 0; i < sizeof(IP); i++)
		IP[i] = strval(sIP[i]);

	return -(IP[0] * 2^24) + (IP[1] * 2^16) + (IP[2] * 2^8) + (IP[3]);
}

stock IsIPInRange(ip[16], range_start[16], range_end[16])
	return ip2int(range_start) <= ip2int(ip) <= ip2int(range_end);
Reply

Quote:
Originally Posted by Matthias134
View Post
What about a function to detect player name whit his id.

Ex: I type id1 in chat, if id1 is named "The_Noob" will display a message in the chat.

Ex2: I type in chat: hi id1
id1 name = The_Noob

I: hi The_Noob

It would be appreciated
It would be so difficult to do that.
I've been working for this one. I will fix it if i've time.

It works fine when typing the numeric numbers.
However, If you type the text not the numeric ones.
It wouldn't display the text parameters OnPlayerText.
It is because it return wrong.

Current i will not give the code i'm current working on it.
Reply

pawn Code:
#if defined __COMPILER_SECOND_PASS
    #define __COMPILER_PASS (1)
#else
    #define __COMPILER_PASS (0)
#endif
#define COMPILER_1ST_PASS (__COMPILER_PASS == (0))
#define COMPILER_2ND_PASS (__COMPILER_PASS == (1))
#define COMPILER_FIRST_PASS  COMPILER_1ST_PASS
#define COMPILER_SECOND_PASS COMPILER_2ND_PASS
static stock __COMPILER_SECOND_PASS() {}
For some reason the compiler makes multiple passes over the code for pre-processing. With this you can detect which pass the code is in, so you can do something like:

pawn Code:
#if COMPILER_1ST_PASS
    #define G 42
#else
    #define G 27
#endif

main()
{
    printf("%d", G);
}
If you compile that code with "-l", which outputs the result of the first pre-processing stage, it will show:

pawn Code:
printf("%d", 42);
But if you run it, it will use the second stage and output:

Code:
27
Just to be awkward and obfuscated!

I didn't actually create this to write obfuscated code. The new version of YSI has VASTLY simplified groups code that makes developing with it FAR easier. One of the features of y_groups is that things like y_commands and y_classes use it ONLY if the scripter explicitly includes it. This is very nice decoupling, and it doesn't matter if they do:

pawn Code:
#include <YSI\y_commands>
#include <YSI\y_groups>
Or:

pawn Code:
#include <YSI\y_groups>
#include <YSI\y_commands>
Either way, it will correctly include all the groups code because they included "y_groups". This looks and behaves very nicely but it took a TON of HORRIBLE code behind the scenes and made the libraries very tightly coupled despite not looking it! The fact that compilers make multiple passes meant that I could change the code to something like this:

pawn Code:
#include "y_groups/setup"
#if defined _YSI_HAS_y_groups
    #define _GROUP_MAKE_NAME<%0...%1> %0Command%1
    #define _GROUP_MAKE_LIMIT         MAX_COMMANDS
    #include "y_groups\_funcs"
#endif
That is down from DOZENS of lines spread over multiple files - if the function "_YSI_HAS_y_groups" is found on the first pass, this code will be included on the second pass. But it isn't actually that simple because the code wasn't included on the first pass. So instead I do:

pawn Code:
#include "y_groups/setup"
#if defined _YSI_HAS_y_groups || COMPILER_1ST_PASS
    #define _GROUP_MAKE_NAME<%0...%1> %0Command%1
    #define _GROUP_MAKE_LIMIT         MAX_COMMANDS
    #include "y_groups\_funcs"
#endif
So on the first compiler pass it includes all the y_groups code for y_commands, even if the scripter doesn't want it. Then, on the second pass, it only includes it if they do.
Reply

Quote:
Originally Posted by _Jake_
View Post
It would be so difficult to do that.
I've been working for this one. I will fix it if i've time.

It works fine when typing the numeric numbers.
However, If you type the text not the numeric ones.
It wouldn't display the text parameters OnPlayerText.
It is because it return wrong.

Current i will not give the code i'm current working on it.
Yes, I know this is a snippet - however it was in response to the quote above! Move if desired!

You mean something like...
pawn Code:
public OnPlayerText(playerid, text[])
{
    new idCheck = strfind(text, "id", true);
    if(idCheck != -1)
    {
        new sid[128];
        strcat(sid, text);
        strdel(sid, 0, idCheck);
        new spacePos = strfind(sid, " ");
        if(spacePos != -1) strdel(sid, spacePos, strlen(sid));
       
        new newString[128], pName[24];
        GetPlayerName(strval(sid[2]), pName, 24);

        strcat(newString, str_replace(sid, pName, text));
        SendPlayerMessageToAll(playerid, newString);
        return 0;
    }
    return 1;
}
You will need your own implementation of str_replace, or use this found in David Weston's strlib library:
pawn Code:
/*
    Snippet taken from:
        strlib library for string manipulation.
        Created by David Weston

        Version: 1.3
        Licence: http://www.typefish.co.uk/licences/
*/

stock str_replace(sSearch[], sReplace[], const sSubject[], &iCount = 0)
{
    new
        iLengthTarget = strlen(sSearch),
        iLengthReplace = strlen(sReplace),
        iLengthSource = strlen(sSubject),
        iItterations = (iLengthSource - iLengthTarget) + 1;

    new
        sTemp[128],
        sReturn[128];

    strcat(sReturn, sSubject, 128);
    iCount = 0;

    for(new iIndex; iIndex < iItterations; ++iIndex)
    {
        strmid(sTemp, sReturn, iIndex, (iIndex + iLengthTarget), (iLengthTarget + 1));

        if(!strcmp(sTemp, sSearch, false))
        {
            strdel(sReturn, iIndex, (iIndex + iLengthTarget));
            strins(sReturn, sReplace, iIndex, iLengthReplace);

            iIndex += iLengthTarget;
            iCount++;
        }
    }

    return sReturn;
}
Reply

This is for people who don't have a site for there server like me -_-


So lets say you don't have a site but you still want feedback from your players or you want them to report bugs and suggest things... this is what you can use...

Quote:

CMD:reportbug(playerid, params[])
{
if (isnull(params)) return SendClientMessage(playerid,-1,# /reportbug <bug> If you abuse this you will serve a punishment.);

new
_msg[ 128 ],
_msg2[ 128 ],
player_name[ MAX_PLAYER_NAME ];

GetPlayerName(playerid, player_name, MAX_PLAYER_NAME);
SendClientMessage(playerid,red,"The bug you have reported has gone through to the main office!");

format( _msg, sizeof ( _msg ), "Name: %s\r\n", player_name);
format( _msg2, sizeof( _msg2), "Bug: %s\r\n",params);

new File: fileToWrite = fopen("Bug Reports.txt", io_append);
fwrite(fileToWrite,"[New Bug]\r\n");
fwrite(fileToWrite, _msg);
fwrite(fileToWrite, _msg2);
fclose(fileToWrite);
return 1;
}

Not all is made by me got a little help but yeah when the player reports a bug it will create a file called Bug Reports.txt in your scriptfiles and then the layout will look like this

[New Bug]
Name: aadasdas
Bug: lol

You will get a lot of spam if you have people abusing this so if anyone can update this one a bit more then it would be a better function
Reply

SetPosInFrontOfPlayer(playerid, giveplayerid, Float:distance);

Code:
SetPosInFrontOfPlayer(playerid, giveplayerid, Float:distance)
{
    new Float:x,Float:y,Float:z,Float:a;
    GetPlayerPos(playerid, x, y,z);
    GetPlayerFacingAngle(playerid, a);
    x += (distance * floatsin(-a, degrees));
    y += (distance * floatcos(-a, degrees));
    SetPlayerPos(giveplayerid,x,y,z);
        SetPlayerFacingAngle(giveplayerid,a);
}
Reply

New version of memset (several previous versions). This function takes an array and sets every element of that array to a given value (0 by default):

pawn Code:
new a[10];
memset(a); // "a" all 0.
memset(a, 4); // "a" all 4.
memset(a, 10, sizeof (a) / 2); // Half of "a" 10.
Slice wrote a version which used the "FILL" PAWN OpCode to do this, which was quite nice and fast, but relied on some run-time code to figure out where the "FILL" command was and change the size parameter. This version uses ZeeX's excellent "amx" library to actually generate a whole new function! The first time "memset" is called it rewrites itself to a stupidly fast version then calls that version; all future times that it is called it is already the fast version so that just gets executed.

To add to the "meta-ness" of the code. The code which is written is self-modifying code - in short I've written code to write code to write code! I said that Slice's version calculates the location of "FILL" every time it is called, and this is required because "FILL" doesn't take a variable it only takes a constant that needs to be changed. This version instead calculates in advance where the "FILL" instruction is and hard-codes that address in to the generated function.

However, you don't really need to worry about ANY of that, all you need to know is how to call it (above). I should note that the parameter order has CHANGED since my last version to a far more sensible order!

For those interested, this is what the final version of the function looks like. There are actually two versions written. "rawMemset" is the version that takes an address and a byte count, "memset" takes an array and a cell slot count so both get rewritten.

pawn Code:
#emit PROC                                // Required, at the start of ALL functions.
#emit LOAD.S.pri size
#emit CONST.alt  0xFFFFFFFC
#emit AND                                 // size &= ~3;
#emit STOR.pri   <FILL parameter address> // What Slice's version calculates.
#emit LOAD.S.alt iValue
#emit LOAD.S.pri iAddress
#emit FILL       0                        // We change the value of "0".
#emit LOAD.pri   <FILL parameter address> // Return the bytes written.
#emit RETN                                // End.
Here is the actual code:

pawn Code:
#tryinclude "..\amx\asm"
#include "amx\asm"

/**--------------------------------------------------------------------------**\
<summary>
    memset
    rawMemset
</summary>
<param name="arr[], iAddress">Array or address to set to a value.</param>
<param name="iValue">What to set the cells to.</param>
<param name="iSize">Number of cells to fill.</param>
<returns>
    -
</returns>
<remarks>
    Based on code by Slice:
   
    https://sampforum.blast.hk/showthread.ph...pid1606781
   
    Modified to use binary flags instead of a loop.
   
    "memset" takes an array, the size of the array, and a value to fill it with
    and sets the whole array to that value.
   
    "rawmemset" is similar, but takes an AMX data segment address instead and
    the size is in bytes, not cells.  However, the size must still be a multiple
    of 4.
</remarks>
\**--------------------------------------------------------------------------**/


stock memset(arr[], val = 0, size = sizeof (arr))
{
    new
        addr;
    #emit LOAD.S.pri arr
    #emit STOR.S.pri addr
    // Convert the size from cells to bytes.
    return rawMemset(addr, val, size * 4);
}

stock rawMemset(iAddress /* 12 */, iValue /* 16 */, iSize /* 20 */)
{
    // They are really, trust me!
    #pragma unused iAddress, iSize, iValue
    // The first time this is called it rewrites itself.  Any other times it is
    // called it just uses the new code.  This is like doing:
    //  
    //  static
    //      bInitialised = false;
    //  if (!bInitialised)
    //  {
    //      // Do something
    //      bInitialised = true;
    //  }
    //  // Do rest.
    //  
    // But better (though FAR more complex).
    // There is NO checking here that we don't write the function bigger than
    // the space available, or even that we don't overwrite "CIP", which would
    // be bad.  The only way to make sure that doesn't happen is write a little
    // with a lot of code!
    new
        base,
        ctx[AsmContext];
    // Get this function.
    #emit CONST.pri rawMemset
    #emit LOAD.alt AMX_HEADER_COD
    #emit ADD
    #emit STOR.S.pri base
    #define _ASM_NO:ctx,) ctx)
    #define _ASM:%0(%1) AsmEmit%0(_:_ASM_NO:ctx,%1)
    AsmInitPtr(ctx, base, 80), // Don't need any more than that.
    // Frankly by this point we have probably already written more code than
    // will be generated!
    _ASM:Proc(),
    _ASM:LoadSPri(20),
    _ASM:ConstAlt(~3),
    _ASM:And(),
    _ASM:StorPri(ctx[AsmContext_buffer] + ctx[AsmContext_buffer_offset] + 7 * 4),
    // The documentation says "PRI" should be a pointer, but that's not true!
    _ASM:LoadSAlt(12),
    _ASM:LoadSPri(16),
    _ASM:Fill(0),
    // Return the bytes filled.
    _ASM:LoadPri(ctx[AsmContext_buffer] + ctx[AsmContext_buffer_offset] - 4),
    _ASM:Retn();
    // Do the second version.
    #emit CONST.pri memset
    #emit LOAD.alt AMX_HEADER_COD
    #emit ADD
    #emit STOR.S.pri base
    AsmInitPtr(ctx, base, 80),
    _ASM:Proc(),
    _ASM:LoadSPri(20),
    _ASM:ShlCPri(2),
    _ASM:StorPri(ctx[AsmContext_buffer] + ctx[AsmContext_buffer_offset] + 7 * 4),
    _ASM:LoadSAlt(12),
    _ASM:LoadSPri(16),
    _ASM:Fill(0),
    _ASM:LoadPri(ctx[AsmContext_buffer] + ctx[AsmContext_buffer_offset] - 4),
    _ASM:Retn();
    #undef _ASM
    #undef _ASM_NO
    // Call this function again (the new version), but don't let the compiler
    // know...  First clear the stack.
    #emit LCTRL      5
    #emit SCTRL      4
    #emit CONST.pri  rawMemset
    #emit ADD.C      4
    #emit SCTRL      6
    // Never hit because of going to an earlier "RETN".
    return memset("", 0, 0);
}
Reply

That's very neat!
Reply

very nice !
Reply

Here's a function which I just created. It's about getting player's class. It auto-sets the class under 'OnPlayerRequestClass' and also sets even if the classes are switched under 'OnPlayerRequestClass' as it's being called on every switches. It's hooked and also provided with 'SetPlayerClass' in case if required to set the player's class. You don't need to SetPlayerClass under OnPlayerRequestClass, as it's already done and hooked.

pawn Код:
new L_PlayerClass_[MAX_PLAYERS];

stock SetPlayerClass(playerid, classid)
{
 L_PlayerClass_[playerid] = classid;
 return 1;
}

stock GetPlayerClass(playerid)
{
 return L_PlayerClass_[playerid];
}

public OnPlayerRequestClass(playerid, classid)
{
 SetPlayerClass(playerid, classid);
 CallLocalFunction("L_OnPRC", "ii", playerid, classid);
 return 1;
}

forward L_OnPRC(playerid, classid);

#if defined _ALS_OnPlayerRequestClass
 #undef OnPlayerRequestClass
#else
 #define _ALS_OnPlayerRequestClass
#endif

#define OnPlayerRequestClass L_OnPRC

Some examples:

pawn Код:
public OnPlayerRequestClass(playerid, classid)
{
 printf("Class: %d", GetPlayerClass(playerid));
 return 1;
}

public OnPlayerSpawn(playerid)
{
 printf("Spawn Class: %d", GetPlayerClass(playerid));
 return 1;
}
Reply

SetPlayerRandomPosInArea generates a random position in a defined area and teleports a player to it.
Parameters: (playerid, Float:minx, Float:miny, Float:maxx, Float:maxy)

pawn Код:
#include <a_samp>

forward FixPos(playerid);
public FixPos(playerid)
{
    new Float:X, Float:Y, Float:Z;
    GetPlayerPos(playerid, X, Y, Z);
    SetPlayerPosFindZ(playerid, X, Y, 800.0);
    return 1;
}

stock randomEx(Float:min, Float:max)
{
    new rand = random(floatround(max-min, floatround_round))+floatround(min, floatround_round);
    return rand;
}

stock SetPlayerRandomPosInArea(playerid, Float:minx, Float:miny, Float:maxx, Float:maxy)
{
    new Float:X, Float:Y;
    X = float(randomEx(minx, maxx));
    Y = float(randomEx(miny, maxy));   
    SetPlayerPosFindZ(playerid, X, Y, 800.0);
    SetTimerEx("FixPos", 300, false, "i", playerid);
    return 1;
}
Reply

DamagePlayer
Parameters: playerid, Float:damage
Function: Damages the player reducing the armour/health. If there's armour for player, it reduces it. If the damage is specified more than the armour, it reduces the health too.

pawn Код:
stock DamagePlayer(playerid, Float:damage)
{
 new Float:temp_hp, Float:temp_arm;
 GetPlayerHealth(playerid, temp_hp);
 GetPlayerArmour(playerid, temp_arm);
 if(temp_arm <= 0)
 {
  SetPlayerHealth(playerid, temp_hp - damage);
 }
 else if(temp_arm >= 1.00)
 {
  new Float:minus_result = temp_arm - damage;
 // printf("%f", minus_result); //For debug.
  if(minus_result <= 0.00)
  {
   SetPlayerArmour(playerid, 0.00);
   SetPlayerHealth(playerid, minus_result + temp_hp);
  }
  else if(minus_result >= 1.00)
  {
   SetPlayerArmour(playerid, minus_result);
  }
 }
 return 1;
}
pawn Код:
//Example:
public OnPlayerDeath(playerid, killerid, reason)
{
 if(killerid != INVALID_PLAYER_ID)
 {
  DamagePlayer(killerid, 20.00); //Reduces 20% of armour/hp.
 }
 return 1;
}
Reply

Useful for getting the text behind an address
pawn Код:
stock GetTextFromAddress(address) {
    #emit load.s.pri address
    #emit stor.s.pri 16
    #emit retn

    new
        tmp[1]
    ;
    return tmp;
}
pawn Код:
new
    text[] = "Hello World",
    addr
;
#emit addr.pri text
#emit stor.s.pri addr

print(GetTextFromAddress(addr)); // "Hello World"
Reply

That's actually very useful! Maybe you can add this to your small library to get the address instead of using emit.
pawn Код:
stock GetAddressFromText(text[]) {
    #emit load.s.pri text
    #emit retn
   
    return 0;
}
pawn Код:
new
    text[] = "Hello World"
;
print(GetTextFromAddress(GetAddressFromText(text))); // "Hello World"
Reply

Quote:
Originally Posted by ******
Посмотреть сообщение
Storing that in address 16 is entirely wrong.
It overwrites the heap address which was passed beforehand because the function is returning a string
Afterwards print does take the new address instead of the heap one :/
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)