[Tutorial] Entrances and Exits: One command, one file!
#1

Tutorial by Jack Leslie
Entrances and Exits: With one command, and one file!

inspired by an idea from dowster.

What do I mean by "one command, one file"?
Well right before I decided to make this tutorial, dowster came up with an idea to have "/door" rather then "/enter" and "/exit", so it would save a lot of time, and it would make your script a bit cleaner. So I thought yeah, it's a good idea, easy done, doesn't take long. Then, in my roleplay gamemode, I've got entrances controlled by having one file and having the script read that file instead of reading hundreds of lines. So, today, I'm going to show you how to do that.

Step 1: the stock.
First off, you're going to need the stock so we can load the information from the file. In my gamemode, it's called "entrances.cfg", so let's name the stock, "LoadEntrances".

pawn Code:
stock LoadEntrances()
{
    new File:file = fopen("entrances.cfg",io_read);
    if(file)
    {
        new
            line[512],
            parts[16][32],
            commentpos = -1,
            count
        ;
        for(new i = 0;i < sizeof(Entrances);i++)
        {
            if(EntranceRef[i][0]) DestroyPickup(EntranceRef[i][0]);
            if(EntranceRef[i][1]) DestroyPickup(EntranceRef[i][1]);
            Entrances[i][outside_icon] = 0;
            Entrances[i][outside_x] = 0;
            Entrances[i][outside_y] = 0;
            Entrances[i][outside_z] = 0;
            Entrances[i][outside_a] = 0;
            Entrances[i][outside_i] = 0;
            Entrances[i][outside_w] = 0;
            Entrances[i][inside_icon] = 0;
            Entrances[i][inside_x] = 0;
            Entrances[i][inside_y] = 0;
            Entrances[i][inside_z] = 0;
            Entrances[i][inside_a] = 0;
            Entrances[i][inside_i] = 0;
            Entrances[i][inside_w] = 0;
        }
        while(fread(file,line) && count < sizeof(Entrances))
        {
            commentpos = strfind(line,"//",true);

            if(commentpos != 0)
            {
                if(commentpos != -1) strmid(line,line,0,commentpos);
                split(line,parts,',');
                Entrances[count][outside_icon] = strval(parts[0]);
                Entrances[count][outside_x] = floatstr(parts[1]);
                Entrances[count][outside_y] = floatstr(parts[2]);
                Entrances[count][outside_z] = floatstr(parts[3]);
                Entrances[count][outside_a] = floatstr(parts[4]);
                Entrances[count][outside_i] = strval(parts[5]);
                Entrances[count][outside_w] = strval(parts[6]);
                Entrances[count][inside_icon] = strval(parts[7]);
                Entrances[count][inside_x] = floatstr(parts[8]);
                Entrances[count][inside_y] = floatstr(parts[9]);
                Entrances[count][inside_z] = floatstr(parts[10]);
                Entrances[count][inside_a] = floatstr(parts[11]);
                Entrances[count][inside_i] = strval(parts[12]);
                Entrances[count][inside_w] = strval(parts[13]);
                EntranceRef[count][0] = CreatePickup(
                    Entrances[count][outside_icon],
                    23,
                    Entrances[count][outside_x],
                    Entrances[count][outside_y],
                    Entrances[count][outside_z],
                    Entrances[count][outside_w]
                );
                EntranceRef[count][1] = CreatePickup(
                    Entrances[count][inside_icon],
                    23,
                    Entrances[count][inside_x],
                    Entrances[count][inside_y],
                    Entrances[count][inside_z],
                    Entrances[count][inside_w]
                );
                count++;
            }
        }
        return 1;
    }
    return 0;
}
Wait! We forgot the enum and a couple of other lines so we can use that stock.

pawn Code:
enum entrancestats { outside_icon , Float:outside_x , Float:outside_y , Float:outside_z , Float:outside_a , outside_i , outside_w , inside_icon , Float:inside_x , Float:inside_y , Float:inside_z , Float:inside_a , inside_i , inside_w , opens , closes }
new Entrances[100][entrancestats];
new EntranceRef[sizeof(Entrances)][2];
Make sure those lines are ABOVE the stock.
Hint: As I'm going along I'm doing exactly what I'm saying into a FilterScript, and at the end will put a link to that FilterScript.

Now we need to load the information from the file into the enums, so we will most likely wanna do this OnGameModeInit or if it's a filterscript, OnFilterScriptInit.
Code:
LoadEntrances();
Now we're nearly done already, but first I'll explain to you how and what to put in the file, "entrances.cfg", otherwise you won't know what the heck you're doing!

Here's one of my lines from my entrances.cfg:
Code:
1239, 1554.690551, -1675.809082, 16.195312, 0.00, 0, 0, 1239, 246.321929, 108.295661, 1003.218750, 0.00, 10, 0 //SAPD
Now I'll explain it.

1. 1239: The outside Pickup ID. (http://weedarr.wikidot.com/pickups) (https://sampwiki.blast.hk/wiki/Pickup_help)
2. 1554.690551: The X for the entrance.
3. -1675.809082: The Y for the entrance.
4. 16.195312: The Z for the entrance.
Hint: 2, 3 and 4 is where you'll enter from, and teleport when you exit.
5. 0.00: The angle the player will face when he comes out of the building.
6. 0: The interior id the player will go into when he exits, in this case it's mostly 0.
7. 0: The virtual world the player will go into when he exits, in this case it can sometimes be more then 0.
8. 1239: The pickup ID that is inside.
9. 246.321929: The X for where the player will enter into.
10. 108.295661: The Y for where the player will enter into.
11. 1003.218750: The Z for where the player will enter into.
12. 0.00: The angle the player will face when he enters.
13. 10: The interior id the player will enter into.
14. 0: The virtual world where the player will enter into.

Now if you didn't notice, 1-7 is really for the outside, and 8-14 is really for the inside. It's easy to get mixed up though, so I hope I explained it good.

Step 2: making the command to enter/exit.
Okay so now we've got the basics of our entrances/exits set up and if you put that line I gave you into the entrances and go to persian square in-game, you should see an icon, but we can't do anything with it at the moment because we haven't got the command, but that's what we're going to do now!

We'll use the command, "/door", an idea from dowster.
So we should have this:
pawn Code:
public OnPlayerCommandText(playerid, cmdtext[])
{
    if (strcmp(cmdtext, "/door", true) == 0)
    {
       
        return 1;
    }
    return 0;
}

Alright, so we have loaded the information from the file into the enums, so now we can check if the player is near one of the entrances, by looking through the enums. We'll use this code:
pawn Code:
for(new i = 0;i < sizeof(EntranceRef);i++)
            {
                if(
                    IsPlayerInRangeOfPoint(playerid,2,Entrances[i][outside_x],Entrances[i][outside_y],Entrances[i][outside_z])
                    &&
                    GetPlayerVirtualWorld(playerid) == Entrances[i][outside_w]
                )
                {
                    SetPlayerPos(playerid,Entrances[i][inside_x],Entrances[i][inside_y],Entrances[i][inside_z]);
                    SetPlayerFacingAngle(playerid,Entrances[i][inside_a]);
                    SetPlayerInterior(playerid,Entrances[i][inside_i]);
                    SetPlayerVirtualWorld(playerid,Entrances[i][inside_w]);
                    SetCameraBehindPlayer(playerid);
                    return 1;
                }
            }
The code uses a radius of 2, and also checks if the player is in the Virtual World we have stored in the file/enums, so we can have more then one command in the same position, but just use different Virtual Worlds. It then sets the players position to what we have in 9, 10 and 11. It would also set the players facing angle from 12, if you have an angle set. If you had common sense, you'd also know that it sets the players interior and virtual world to what we have in 13 and 14.

So now we should have:
pawn Code:
public OnPlayerCommandText(playerid, cmdtext[])
{
    if (strcmp(cmdtext, "/door", true) == 0)
    {
    for(new i = 0;i < sizeof(EntranceRef);i++)
    {
        if(
            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][outside_x],Entrances[i][outside_y],Entrances[i][outside_z])
            &&
            GetPlayerVirtualWorld(playerid) == Entrances[i][outside_w]
            )
                {
                    SetPlayerPos(playerid,Entrances[i][inside_x],Entrances[i][inside_y],Entrances[i][inside_z]);
                    SetPlayerFacingAngle(playerid,Entrances[i][inside_a]);
                    SetPlayerInterior(playerid,Entrances[i][inside_i]);
                    SetPlayerVirtualWorld(playerid,Entrances[i][inside_w]);
                    SetCameraBehindPlayer(playerid);
                    return 1;
                }
            }
    return 1;
    }
    return 0;
}
Now we can go to that pickup, type "/door" and we will be inside an interior! Let's move right along and make it so we can get back outside.

The code:
pawn Code:
for(new i = 0;i < sizeof(EntranceRef);i++)
                {
                    if(
                            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][inside_x],Entrances[i][inside_y],Entrances[i][inside_z])
                            &&
                            GetPlayerVirtualWorld(playerid) == Entrances[i][inside_w]
                    )
                    {
                        SetPlayerPos(playerid,Entrances[i][outside_x],Entrances[i][outside_y],Entrances[i][outside_z]);
                        SetPlayerFacingAngle(playerid,Entrances[i][outside_a]);
                        SetPlayerInterior(playerid,Entrances[i][outside_i]);
                        SetPlayerVirtualWorld(playerid,Entrances[i][outside_w]);
                        return 1;
                    }
                }
It does the same as what our entrance code does, but uses the opposite information to teleport the player.
Now you should have:
pawn Code:
public OnPlayerCommandText(playerid, cmdtext[])
{
    if (strcmp(cmdtext, "/door", true) == 0)
    {
        for(new i = 0;i < sizeof(EntranceRef);i++)
        {
            if(
                IsPlayerInRangeOfPoint(playerid,2,Entrances[i][outside_x],Entrances[i][outside_y],Entrances[i][outside_z])
                &&
                GetPlayerVirtualWorld(playerid) == Entrances[i][outside_w]
                )
                    {
                        SetPlayerPos(playerid,Entrances[i][inside_x],Entrances[i][inside_y],Entrances[i][inside_z]);
                        SetPlayerFacingAngle(playerid,Entrances[i][inside_a]);
                        SetPlayerInterior(playerid,Entrances[i][inside_i]);
                        SetPlayerVirtualWorld(playerid,Entrances[i][inside_w]);
                        SetCameraBehindPlayer(playerid);
                        return 1;
                    }
                }
        for(new i = 0;i < sizeof(EntranceRef);i++)
                {
                    if(
                            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][inside_x],Entrances[i][inside_y],Entrances[i][inside_z])
                            &&
                            GetPlayerVirtualWorld(playerid) == Entrances[i][inside_w]
                    )
                    {
                        SetPlayerPos(playerid,Entrances[i][outside_x],Entrances[i][outside_y],Entrances[i][outside_z]);
                        SetPlayerFacingAngle(playerid,Entrances[i][outside_a]);
                        SetPlayerInterior(playerid,Entrances[i][outside_i]);
                        SetPlayerVirtualWorld(playerid,Entrances[i][outside_w]);
                        return 1;
                    }
                }
        return 1;
    }
    return 0;
}
Now you should be able to type "/door" to enter and exit from a building! But wait, we haven't finished yet! I've got a little extra goodies. One of the advantages of doing this with a file, is that you don't have to restart your server to load the information. We can use one command, that will reload all information from the file and into the enums, so we can use it straight away.

The reloading command:
pawn Code:
if(strcmp(cmdtext, "/reloade", true) == 0)
    {
        if(IsPlayerAdmin(playerid))
        {
            LoadEntrances();
            SendClientMessage(playerid, -1, "Entrances reloaded!");
            return 1;
        }
    }
This is an RCON command, so if you want, just change IsPlayerAdmin(playerid) into your Admin Variable.

Giving a "type /door to enter" message:
Seen those scripts that have "Type /enter to enter" or something similar? Well you can have the same thing. We'll just use a code to ask if the player is standing in range of the entrance, and then tell him to type "/door" to enter, in case a new player thinks you type "/enter".

Here's out code:
pawn Code:
for(new i = 0;i < sizeof(EntranceRef);i++)
    {
        if(
            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][outside_x],Entrances[i][outside_y],Entrances[i][outside_z])
            &&
            GetPlayerVirtualWorld(playerid) == Entrances[i][outside_w]
        )
        {
            GameTextForPlayer(playerid, "~w~Type ~r~/enter~w~ to go inside", 5000, 5);
            return 1;
        }
    }
And we should place that under:
Code:
public OnPlayerPickUpPickup(playerid, pickupid)
Giving us:
pawn Code:
public OnPlayerPickUpPickup(playerid, pickupid)
{
    for(new i = 0;i < sizeof(EntranceRef);i++)
    {
        if(
            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][outside_x],Entrances[i][outside_y],Entrances[i][outside_z])
            &&
            GetPlayerVirtualWorld(playerid) == Entrances[i][outside_w]
        )
        {
            GameTextForPlayer(playerid, "~w~Type ~r~/door~w~ to go inside", 5000, 5);
            return 1;
        }
    }
    return 1;
}
Want one for the inside, telling him to type "/door to exit"?
Then just add the same code, but asking if he's in range of the exit:
pawn Code:
for(new i = 0;i < sizeof(EntranceRef);i++)
    {
        if(
            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][inside_x],Entrances[i][inside_y],Entrances[i][inside_z])
            &&
            GetPlayerVirtualWorld(playerid) == Entrances[i][inside_w]
        )
        {
            GameTextForPlayer(playerid, "~w~Type ~r~/door~w~ to go outside", 5000, 5);
            return 1;
        }
    }
Giving us:
pawn Code:
public OnPlayerPickUpPickup(playerid, pickupid)
{
    for(new i = 0;i < sizeof(EntranceRef);i++)
    {
        if(
            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][outside_x],Entrances[i][outside_y],Entrances[i][outside_z])
            &&
            GetPlayerVirtualWorld(playerid) == Entrances[i][outside_w]
        )
        {
            GameTextForPlayer(playerid, "~w~Type ~r~/door~w~ to go inside", 5000, 5);
            return 1;
        }
    }
    for(new i = 0;i < sizeof(EntranceRef);i++)
    {
        if(
            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][inside_x],Entrances[i][inside_y],Entrances[i][inside_z])
            &&
            GetPlayerVirtualWorld(playerid) == Entrances[i][inside_w]
        )
        {
            GameTextForPlayer(playerid, "~w~Type ~r~/door~w~ to go outside", 5000, 5);
            return 1;
        }
    }
    return 1;
}

So now, we should have a one command, file controlled entrance/exit system, that we can reload at anytime without restarting the server. The only trouble you might have, is getting confused to what order the goes into the file, but after doing it a fair few times, you start to memorize it.

Do you remember me saying I was doing all this into a filterscript as I was going along? I done that to make sure if you followed what I said, then you should be able to do it without any errors, and everytime I done a step, I tested it to make sure it works, and guess what? It worked for me. Although, you might of had an "undefined sysmbol 'split" error, if so, then you don't have the split function, put it ABOVE the LoadEntrances stock:
pawn Code:
stock split(const strsrc[], strdest[][], delimiter)
{
    new i, li;
    new aNum;
    new len;
    while(i <= strlen(strsrc)){
        if(strsrc[i]==delimiter || i==strlen(strsrc)){
            len = strmid(strdest[aNum], strsrc, li, i, 128);
            strdest[aNum][len] = 0;
            li = i+1;
            aNum++;
        }
        i++;
    }
    return 1;
}

FilterScript: http://pastebin.com/ukwRSnfQ
Credits:
Tutorial - Jack Leslie
"/door" idea - dowster
Coding - mentioned in the FilterScript.
Reply
#2

Err, I think you only explained how to add entrances in the .cfg file.

Double loops
pawn Code:
public OnPlayerPickUpPickup(playerid, pickupid)
{
    for(new i = 0;i < sizeof(EntranceRef);i++)
    {
        if(
            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][outside_x],Entrances[i][outside_y],Entrances[i][outside_z])
            &&
            GetPlayerVirtualWorld(playerid) == Entrances[i][outside_w]
        )
        {
            GameTextForPlayer(playerid, "~w~Type ~r~/door~w~ to go inside", 5000, 5);
            return 1;
        }
    }
    for(new i = 0;i < sizeof(EntranceRef);i++)
    {
        if(
            IsPlayerInRangeOfPoint(playerid,2,Entrances[i][inside_x],Entrances[i][inside_y],Entrances[i][inside_z])
            &&
            GetPlayerVirtualWorld(playerid) == Entrances[i][inside_w]
        )
        {
            GameTextForPlayer(playerid, "~w~Type ~r~/door~w~ to go outside", 5000, 5);
            return 1;
        }
    }
    return 1;
}
Rating how do you explain : 2.5/10

Nice code though.
Reply
#3

Didn't even notice the double loop

Thanks for your feedback, even if it was 2.5, I'm new to tutorials :P
Reply
#4

Maybe i shouldn't be so lazy when i think of something? lol I'd been using /door for a while but never bothered to suggest it on forums. Glad you made this and good job on the tut, well explained but maybe adding some comments in the pawn code areas to explain what each thing is doing.
Reply
#5

Lol. I found the exact same thing in the SARP r112 script. Good job anyway.
Reply
#6

Quote:
Originally Posted by 69Playa
View Post
Lol. I found the exact same thing in the SARP r112 script. Good job anyway.
Yeah that's where the LoadEntrances(); came from, but a lot of people done scripts for SARP r112 so I couldn't give who exactly did it credit, but I just made this tutorial to tell people how to use it :P
Reply
#7

Seems quite useful mate, good job.
Reply
#8

Quote:
Originally Posted by Jack_Leslie
View Post
Yeah that's where the LoadEntrances(); came from, but a lot of people done scripts for SARP r112 so I couldn't give who exactly did it credit, but I just made this tutorial to tell people how to use it :P
Shanks Wizzle/Smo Wang/Dion Slater.

Good job. Hell, I'm even using this!
Reply
#9

Quote:
Originally Posted by 69Playa
View Post
Shanks Wizzle/Smo Wang/Dion Slater.

Good job. Hell, I'm even using this!
Well there ya go.

Thanks for the feedback
Reply
#10

Man you rock! I'll be using this as soon as I start my project(started).
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)