[Tutorial] Advanced '/spec' Command [Updated 11/21/2011 | Virtual World & Interior Support | Beginner Friendly]
#1

Simple Spec Command


I've seen many spec commands that either doesn't: Change interior, virtual words, spec a vehicle or change the spectating type from player to vehicle and visa versa. Well after you're finished with this tutorial then you'll be able to do just that.

- Functions

Listed below are the main functions we're gonna be using in this command, I recommend you read more on them (link provided)

- TogglePlayerSpectating(playerid, toggle); - Read more here!
- PlayerSpectateVehicle(playerid, targetvehicleid);Read more here!
- PlayerSpectatePlayer(playerid,tagetid);Read more here!


- Includes

Also listed below are the two main includes we will be using.

ZCMD - This is a command processor made by Zeex, it's mostly recommended that you use it because it's fast, simple, easy and beginner friendly.

sscanf - Now this is mostly if not always, used in cooperation with zcmd to make cool commands, such as the one we're making, it was made by ******.

foreach - It's a loop faster than the for loop, with some added features.

ZCMD Download
sscanf Download
foreach Download

- Coding

We're gonna need to create some variables, please make them global - meaning outside of a function or callback but at the top of your script

pawn Code:
#include <a_samp>
#include <zcmd>
#include <sscanf> // Since you might have the newer version of sscanf it would be '#include <sscanf2>'

#undef MAX_PLAYERS
#define MAX_PLAYERS 32  // Here you need to set the maximum number of players able to play on your server at any given time, I'll put 32 because that's the max players on my server.
#define Grey 0xC0C0C0FF // Defining the color 'Grey'

new String[128], Float:SpecX[MAX_PLAYERS], Float:SpecY[MAX_PLAYERS], Float:SpecZ[MAX_PLAYERS], vWorld[MAX_PLAYERS], Inter[MAX_PLAYERS];
new IsSpecing[MAX_PLAYERS], Name[MAX_PLAYER_NAME], IsBeingSpeced[MAX_PLAYERS],spectatorid[MAX_PLAYERS];
Now that we have created our includes and all, let's get to creating our command.

pawn Code:
COMMAND:spec(playerid, params[])
{
    new id;// This will hold the ID of the player you are going to be spectating.
    if(!IsPlayerAdmin(playerid))return 0;// This checks if the player is logged into RCON, if not it will return 0; (Showing "SERVER: Unknown Command") You can replace it with your own admin check.
    if(sscanf(params,"u", id))return SendClientMessage(playerid, Grey, "Usage: /spec [id]");// Now this is where we use sscanf to check if the params were filled, if not we'll ask you to fill them
    if(id == playerid)return SendClientMessage(playerid,Grey,"You cannot spec yourself.");// Just making sure.
    if(id == INVALID_PLAYER_ID)return SendClientMessage(playerid, Grey, "Player not found!");// This is to ensure that you don't fill the param with an invalid player id.
    if(IsSpecing[playerid] == 1)return SendClientMessage(playerid,Grey,"You are already specing someone.");// This will make you not automatically spec someone else by mistake.
    GetPlayerPos(playerid,SpecX[playerid],SpecY[playerid],SpecZ[playerid]);// This is getting and saving the player's position in a variable so they'll respawn at the same place they typed '/spec'
    Inter[playerid] = GetPlayerInterior(playerid);// Getting and saving the interior.
    vWorld[playerid] = GetPlayerVirtualWorld(playerid);//Getting and saving the virtual world.
    TogglePlayerSpectating(playerid, true);// Now before we use any of the 3 functions listed above, we need to use this one. It turns the spectating mode on.
    if(IsPlayerInAnyVehicle(id))//Checking if the player is in a vehicle.
    {
        if(GetPlayerInterior(id) > 0)//If the player's interior is more than 0 (the default) then.....
        {
            SetPlayerInterior(playerid,GetPlayerInterior(id));//.....set the spectator's interior to that of the player being spectated.
        }
        if(GetPlayerVirtualWorld(id) > 0)//If the player's virtual world is more than 0 (the default) then.....
        {
            SetPlayerVirtualWorld(playerid,GetPlayerVirtualWorld(id));//.....set the spectator's virtual world to that of the player being spectated.
        }
        PlayerSpectateVehicle(playerid,GetPlayerVehicleID(id));// Now remember we checked if the player is in a vehicle, well if they're in a vehicle then we'll spec the vehicle.
    }
    else// If they're not in a vehicle, then we'll spec the player.
    {
        if(GetPlayerInterior(id) > 0)
        {
            SetPlayerInterior(playerid,GetPlayerInterior(id));
        }
        if(GetPlayerVirtualWorld(id) > 0)
        {
            SetPlayerVirtualWorld(playerid,GetPlayerVirtualWorld(id));
        }
        PlayerSpectatePlayer(playerid,id);// Letting the spectator spec the person and not a vehicle.
    }
    GetPlayerName(id, Name, sizeof(Name));//Getting the name of the player being spectated.
    format(String, sizeof(String),"You have started to spectate %s.",Name);// Formatting a string to send to the spectator.
    SendClientMessage(playerid,0x0080C0FF,String);//Sending the formatted message to the spectator.
    IsSpecing[playerid] = 1;// Just saying that the spectator has begun to spectate someone.
    IsBeingSpeced[id] = 1;// Just saying that a player is being spectated (You'll see where this comes in)
    spectatorid[playerid] = id;// Saving the spectator's id into this variable.
    return 1;// Returning 1 - saying that the command has been sent.
}
That above is the spec command with the explanations. Moving on to our specoff command.

pawn Code:
COMMAND:specoff(playerid, params[])
{
    if(!IsPlayerAdmin(playerid))return 0;// This checks if the player is logged into RCON, if not it will return 0; (Showing "SERVER: Unknown Command")
    if(IsSpecing[playerid] == 0)return SendClientMessage(playerid,Grey,"You are not spectating anyone.");
    TogglePlayerSpectating(playerid, 0);//Toggling spectate mode, off. Note: Once this is called, the player will be spawned, there we'll need to reset their positions, virtual world and interior to where they typed '/spec'
    return 1;
}
That above is the specoff command with the explanations. Moving on to adding some checks to some callbacks.

pawn Code:
public OnPlayerDisconnect(playerid, reason)
{
    if(IsBeingSpeced[playerid] == 1)//If the player being spectated, disconnects, then turn off the spec mode for the spectator.
    {
        foreach(Player,i)
        {
            if(spectatorid[i] == playerid)
            {
                TogglePlayerSpectating(i,false);// This justifies what's above, if it's not off then you'll be either spectating your connect screen, or somewhere in blueberry (I don't know why)
            }
        }
    }
    return 1;
}
The OnPlayerDeath callback will do the same as the OnPlayerDisconnectCallback.

pawn Code:
public OnPlayerDeath(playerid, killerid, reason)
{
    if(IsBeingSpeced[playerid] == 1)//If the player being spectated, dies, then turn off the spec mode for the spectator.
    {
        foreach(Player,i)
        {
            if(spectatorid[i] == playerid)
            {
                TogglePlayerSpectating(i,false);// This justifies what's above, if it's not off then you'll be either spectating your connect screen, or somewhere in blueberry (I don't know why)
            }
        }
    }
    return 1;
}
Now the OnPlayerStateChangecallback, this is where we'll change the spectate from Player to Vehicle or Vehicle to Player.

pawn Code:
public OnPlayerStateChange(playerid, newstate, oldstate)
{
    if(newstate == PLAYER_STATE_DRIVER || newstate == PLAYER_STATE_PASSENGER)// If the player's state changes to a vehicle state we'll have to spec the vehicle.
    {
        if(IsBeingSpeced[playerid] == 1)//If the player being spectated, enters a vehicle, then let the spectator spectate the vehicle.
        {
            foreach(Player,i)
            {
                if(spectatorid[i] == playerid)
                {
                    PlayerSpectateVehicle(i, GetPlayerVehicleID(playerid));// Letting the spectator, spectate the vehicle of the player being spectated (I hope you understand this xD)
                }
            }
        }
    }
    if(newstate == PLAYER_STATE_ONFOOT)
    {
        if(IsBeingSpeced[playerid] == 1)//If the player being spectated, exists a vehicle, then let the spectator spectate the player.
        {
            foreach(Player,i)
            {
                if(spectatorid[i] == playerid)
                {
                    PlayerSpectatePlayer(i, playerid);// Letting the spectator, spectate the player who exited the vehicle.
                }
            }
        }
    }
    return 1;
}
Now we're done with the spec functions, below we'll update the interiors and virtual worlds; Just to make sure we have a clean and cool spec command

pawn Code:
public OnPlayerInteriorChange(playerid, newinteriorid, oldinteriorid)//This is called when a player's interior is changed.
{
    if(IsBeingSpeced[playerid] == 1)//If the player being spectated, changes an interior, then update the interior and virtualword for the spectator.
    {
        foreach(Player,i)
        {
            if(spectatorid[i] == playerid)
            {
                SetPlayerInterior(i,GetPlayerInterior(playerid));
                SetPlayerVirtualWorld(i,GetPlayerVirtualWorld(playerid));
            }
        }
    }
    return 1;
}
We're almost done, just one important thing we could've left out, out OnPlayerSpawn callback.

pawn Code:
public OnPlayerSpawn(playerid)
{
    if(IsSpecing[playerid] == 1)
    {
        SetPlayerPos(playerid,SpecX[playerid],SpecY[playerid],SpecZ[playerid]);// Remember earlier we stored the positions in these variables, now we're gonna get them from the variables.
        SetPlayerInterior(playerid,Inter[playerid]);//Setting the player's interior to when they typed '/spec'
        SetPlayerVirtualWorld(playerid,vWorld[playerid]);//Setting the player's virtual world to when they typed '/spec'
        IsSpecing[playerid] = 0;//Just saying you're free to use '/spec' again YAY :D
        IsBeingSpeced[spectatorid[playerid]] = 0;//Just saying that the player who was being spectated, is not free from your stalking >:D
    }
    return 1;
}
- Conclusion

I really hope this helps someone to make a spec command, and maybe teach them something extra.

- Credits

Zeex - ZCMD
****** - sscanf
****** - foreach
SA-MP Team - SA-MP
You guys - Reading it
Reply
#2

Nice job
Reply
#3

Great tutorial.
Reply
#4

Nice tutorial however, regarding to such a simple command is probably not enough for something being advanced.. How about making spectating with keys? Left and right to go to certain players, pretty simple but yeah, i'd call such a thing advanced ^^.
Reply
#5

Good Job.
Reply
#6

Quote:
Originally Posted by Lorenc_
View Post
Nice tutorial however, regarding to such a simple command is probably not enough for something being advanced.. How about making spectating with keys? Left and right to go to certain players, pretty simple but yeah, i'd call such a thing advanced ^^.
Most '/spec' commands don't support interior and virtual world changing.
Reply
#7

You forgot to set the interior and world variables
Reply
#8

Quote:
Originally Posted by Lorenc_
View Post
Nice tutorial however, regarding to such a simple command is probably not enough for something being advanced.. How about making spectating with keys? Left and right to go to certain players, pretty simple but yeah, i'd call such a thing advanced ^^.
something like that would really be an advanced system, although I guess it would be more suitable in a filterscript...
anyways nice tutorial
Reply
#9

Nice tutorial, however I wouldn't use sscanf for such a thing.

pawn Code:
CMD:spec(playerid, params[])
{
    new id = strlen( params );
    if( isnull( params )) return SendClientMessage( playerid, -1, "Syntax Error: /spec [ID].");
    if(! IsPlayerConnected( id )) return SendClientMessage( playerid, -1, "Syntax Error: /spec [ID].");
    // Gah
}
Reply
#10

Quote:
Originally Posted by Jochemd
View Post
Nice tutorial, however I wouldn't use sscanf for such a thing.

pawn Code:
CMD:spec(playerid, params[])
{
    new id = strlen( params );
    if( isnull( params )) return SendClientMessage( playerid, -1, "Syntax Error: /spec [ID].");
    if(! IsPlayerConnected( id )) return SendClientMessage( playerid, -1, "Syntax Error: /spec [ID].");
    // Gah
}
sscanf would be the best choice. 'u' specifier is names/ids. The other way of doing the same thing is;

pawn Code:
if(isnumeric(params))
{
    id = strval(params);
}
else
{
    new
        szName[MAX_PLAYER_NAME];
    id = INVALID_PLAYER_ID;
   
    for(new i=0; i < MAX_PLAYERS; ++i)
    {
        if(!IsPlayerConnected(i))continue;
        GetPlayerName(i, szName, MAX_PLAYER_NAME);
        if(!strcmp(szName, params))
        {
            id = i;
            break;
        }
    }
}
I prefer one sscanf line to that.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)