[Tutorial] How to Create Dynamic Gates, and Load Mapping Efficiently
#1

Hey everyone,

I released a filterscript that would allow you to easily add mappings, however I've had a bit of confusion about it and a lot of people still ask about dynamic gates etc. I've decided to write this tutorial to explain it in depth.

Adding Objects

The best way to add objects to your server, is using incognito's streamer, in a separate filterscript (This will allow you to reload all mappings on the fly)

Start by downloading streamer if you haven't already: https://sampforum.blast.hk/showthread.php?tid=102865

What the streamer will do is allow you to create an object in a specific virtual world and interior, which is perfect if you want to add extra mapping in one particular house, but not all with the same interior.

The next step is to set up your filterscript, you're going to need a few includes. Because there will be commands later on, you should also get sscanf by ******, and zcmd by ZEEX.

https://sampforum.blast.hk/showthread.php?tid=91354
https://sampforum.blast.hk/showthread.php?tid=120356

Alright, let's get our filterscript started.

pawn Код:
#include <a_samp>
#include <streamer>
#include <sscanf2>
#include <zcmd>

public OnFilterScriptInit()
{
    return 1;
}

public OnPlayerConnect(playerid)
{
    return 1;
}

public OnFilterScriptExit()
{
    for(new i = 0; i < MAX_OBJECTS; i++) //loop through all objects
    {
        if(IsValidObject(i)) //determine if the object exists
        {
            DestroyDynamicObject(i); //remove it if it does
    }
    }
    print("Mapping Unloaded"); //print a message showing success
}
Start by adding the following public functions: OnFilterScriptInit, OnFilterScriptExit, OnPlayerConnect.

The proper format for dynamic objects is as follows:

pawn Код:
CreateDynamicObject(modelid, Float:x, Float:y, Float:z, Float:rx, Float:ry, Float:rz, interiorid, worldid, -1); //the -1 is for playerid, and should always remain at -1.
You can use http://convertffs.com to convert MTA objects into dynamic objects. By setting the interiorid and worldid, you allow the object to only appear in that interior/world. It reduces lag.

Place any objects under OnFilterScriptInit.

If you have any RemoveBuildingForPlayer objects, place them under OnPlayerConnect (Note: You will have to restart the server for these to take effect, as any connected players will not see the changes)

Your script should now look like this:
pawn Код:
public OnFilterScriptInit()
{
    CreateDynamicObject(6959, -2132.76, -2324.35, 29.45,   0.00, 0.00, 52.12, 0, 0, -1); //an example object, place all objects in here.

    return 1;
}

public OnPlayerConnect(playerid)
{
    RemoveBuildingForPlayer(playerid, 1413, -2098.5469, -2222.8984, 30.8984, 0.25); //again, an example object, place all removes in here.
    return 1;
}

public OnFilterScriptExit()
{
    for(new i = 0; i < MAX_OBJECTS; i++) //loop through all objects
    {
        if(IsValidObject(i)) //determine if the object exists
        {
            DestroyDynamicObject(i); //remove it if it does
    }
    }
    print("Mapping Unloaded"); //print a message showing success
}
Now, this is a complete mapping system. But what if you want to add gates? You could script a long command with each gate manually, but that is inefficient and very time consuming, especially if you have many to do. This function I made will help us get past it.

At the top of your script, just below the includes, add the following define and array.

pawn Код:
#define MAX_GATES 999 //change this number to the maximum amount of gates you want to allow in your server.  Beware that if you set this number lower than the gates in the server (for example you set max gates to 40, and you have 45 gates) the last 5 gates will not load.
enum GateInfo
{
    gmodel,
    gobject,
    Float:gclosedx,
    Float:gclosedy,
    Float:gclosedz,
    Float:gclosedrx,
    Float:gclosedry,
    Float:gclosedrz,
    Float:gopenx,
    Float:gopeny,
    Float:gopenz,
    Float:gopenrx,
    Float:gopenry,
    Float:gopenrz,
    gcode[6],
    gclosed,
    gopening,
    gvw,

};

#define DIALOG_CODE 392 //if this dialog ID is in use, the code will not work, make sure to set this to an OPEN dialog.



new GInfo[MAX_GATES][GateInfo];
new gatescreated;
Also note that it is possible to add different types so factions can have their own etc. However, everybody's system is different so I will not go into detail about that.

Alright, so your script should now appear like this:


pawn Код:
#include <a_samp>
#include <streamer>
#include <sscanf2>
#include <zcmd>

#define MAX_GATES 999 //change this number to the maximum amount of gates you want to allow in your server.  Beware that if you set this number lower than the gates in the server (for example you set max gates to 40, and you have 45 gates) the last 5 gates will not load.
enum GateInfo
{
    gmodel,
    gobject,
    Float:gclosedx,
    Float:gclosedy,
    Float:gclosedz,
    Float:gclosedrx,
    Float:gclosedry,
    Float:gclosedrz,
    Float:gopenx,
    Float:gopeny,
    Float:gopenz,
    Float:gopenrx,
    Float:gopenry,
    Float:gopenrz,
    gcode[6],
    gclosed,
    gopening,
    gvw,

};

#define DIALOG_CODE 392 //if this dialog ID is in use, the code will not work, make sure to set this to an OPEN dialog.



new GInfo[MAX_GATES][GateInfo];
new gatescreated;


public OnFilterScriptInit()
{
    CreateDynamicObject(6959, -2132.76, -2324.35, 29.45,   0.00, 0.00, 52.12, 0, 0, -1); //an example object, place all objects in here.

    return 1;
}

public OnPlayerConnect(playerid)
{
    RemoveBuildingForPlayer(playerid, 1413, -2098.5469, -2222.8984, 30.8984, 0.25); //again, an example object, place all removes in here.
    return 1;
}

public OnFilterScriptExit()
{
    for(new i = 0; i < MAX_OBJECTS; i++) //loop through all objects
    {
        if(IsValidObject(i)) //determine if the object exists
        {
            DestroyDynamicObject(i); //remove it if it does
    }
    }
    print("Mapping Unloaded"); //print a message showing success
}
We now have to add our custom function to the bottom of the script, below OnFilterScriptExit. The reason for putting "stock" here, is so you do not get a warning in the event that you attempt to compile without gates.

pawn Код:
stock CreateMovingObject(modelid, Float:closedx, Float:closedy, Float:closedz, Float:closedrx, Float:closedry, Float:closedrz, Float:openx, Float:openy, Float:openz, Float:openrx, Float:openry, Float:openrz, vw, code[6])
{
    gatescreated ++;
    new id = gatescreated;
    GInfo[id][gmodel] = modelid;
    GInfo[id][gclosedx] = closedx;
    GInfo[id][gclosedy] = closedy;
    GInfo[id][gclosedz] = closedz;
    GInfo[id][gclosedrx] = closedrx;
    GInfo[id][gclosedry] = closedry;
    GInfo[id][gclosedrz] = closedrz;
    GInfo[id][gopenx] = openx;
    GInfo[id][gopeny] = openy;
    GInfo[id][gopenz] = openz;
    GInfo[id][gopenrx] = openrx;
    GInfo[id][gopenry] = openry;
    GInfo[id][gopenrz] = openrz;
    GInfo[id][gvw] = vw;
    GInfo[id][gcode] = code;
    return 1;
}
This is the code that you will use to create your gates later. To create a gate, fill in the above parameters, and place the code in OnFilterScriptInit.

You will now need to add the following:

pawn Код:
stock strmatch(const String1[], const String2[])//credits to cameltoe
{
    if ((strcmp(String1, String2, true, strlen(String2)) == 0) && (strlen(String2) == strlen(String1)))
    {
        return true;
    }
    else
    {
        return false;
    }
}
This is for later.

Now, right below OnFilterScriptExit, we have to add our command.

pawn Код:
CMD:gate(playerid)
{
    for(new i=0; i<MAX_GATES; i++)
    {
        if(IsPlayerInRangeOfPoint(playerid, 5.0, GInfo[i][gclosedx], GInfo[i][gclosedy], GInfo[i][gclosedz]))
        {
            if(GInfo[i][gclosed] == 1)
            {
                ShowPlayerDialog(playerid, DIALOG_CODE, DIALOG_STYLE_INPUT, "Code", "This door is password protected.\nEnter the code.", "Enter", "");
                GInfo[i][gclosed] = 0;
                GInfo[i][gopening] = 0;
            }
            else
            {
                MoveDynamicObject(GInfo[i][gobject], GInfo[i][gclosedx], GInfo[i][gclosedy], GInfo[i][gclosedz], 3.0, GInfo[i][gclosedrx], GInfo[i][gclosedry], GInfo[i][gclosedrz]);
                GInfo[i][gclosed] = 1;
            }
        }
    }
    return 1;
}
Next, we need to add our dialog response, right under the command.

pawn Код:
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    if(dialogid == DIALOG_CODE)
    {
        if(response)
        {
            for(new i=0; i<MAX_GATES; i++)
            {
                if(strmatch(inputtext, GInfo[i][gcode]))
                {
                    MoveDynamicObject(GInfo[i][gobject], GInfo[i][gopenx], GInfo[i][gopeny], GInfo[i][gopenz], 3.0, GInfo[i][gopenrx], GInfo[i][gopenry], GInfo[i][gopenrz]);
                    GInfo[i][gclosed] = 0;
                    GInfo[i][gopening] = 0;
                }
            }
        }
    }
    return 0;
}
Finally, we need a way to load the gates. At the bottom of OnFilterScriptInit, add the following code:

pawn Код:
for(new i=0; i<MAX_GATES; i++)
    {
        GInfo[i][gobject] = CreateDynamicObject(GInfo[i][gmodel], GInfo[i][gclosedx], GInfo[i][gclosedy], GInfo[i][gclosedz], GInfo[i][gclosedrx], GInfo[i][gclosedry], GInfo[i][gclosedrz], GInfo[i][gvw], -1, -1);
        GInfo[i][gclosed] = 1;
    }
That's it! To open a gate, you walk up to it and enter the command (/gate) then it will prompt you to enter the code, which you set.

Your script should look like this:

pawn Код:
#include <a_samp>
#include <streamer>
#include <sscanf2>
#include <zcmd>

#define MAX_GATES 999 //change this number to the maximum amount of gates you want to allow in your server.  Beware that if you set this number lower than the gates in the server (for example you set max gates to 40, and you have 45 gates) the last 5 gates will not load.
enum GateInfo
{
    gmodel,
    gobject,
    Float:gclosedx,
    Float:gclosedy,
    Float:gclosedz,
    Float:gclosedrx,
    Float:gclosedry,
    Float:gclosedrz,
    Float:gopenx,
    Float:gopeny,
    Float:gopenz,
    Float:gopenrx,
    Float:gopenry,
    Float:gopenrz,
    gcode[6],
    gclosed,
    gopening,
    gvw,

};

#define DIALOG_CODE 392 //if this dialog ID is in use, the code will not work, make sure to set this to an OPEN dialog.



new GInfo[MAX_GATES][GateInfo];
new gatescreated;


public OnFilterScriptInit()
{
    CreateDynamicObject(6959, -2132.76, -2324.35, 29.45,   0.00, 0.00, 52.12, 0, 0, -1); //an example object, place all objects in here.


    for(new i=0; i<MAX_GATES; i++)
    {
        GInfo[i][gobject] = CreateDynamicObject(GInfo[i][gmodel], GInfo[i][gclosedx], GInfo[i][gclosedy], GInfo[i][gclosedz], GInfo[i][gclosedrx], GInfo[i][gclosedry], GInfo[i][gclosedrz], GInfo[i][gvw], -1, -1);
        GInfo[i][gclosed] = 1;
    }

    return 1;
}

public OnPlayerConnect(playerid)
{
    RemoveBuildingForPlayer(playerid, 1413, -2098.5469, -2222.8984, 30.8984, 0.25); //again, an example object, place all removes in here.
    return 1;
}

public OnFilterScriptExit()
{
    for(new i = 0; i < MAX_OBJECTS; i++) //loop through all objects
    {
        if(IsValidObject(i)) //determine if the object exists
        {
            DestroyDynamicObject(i); //remove it if it does
    }
    }
    print("Mapping Unloaded"); //print a message showing success
}

CMD:gate(playerid)
{
    for(new i=0; i<MAX_GATES; i++)
    {
        if(IsPlayerInRangeOfPoint(playerid, 5.0, GInfo[i][gclosedx], GInfo[i][gclosedy], GInfo[i][gclosedz]))
        {
            if(GInfo[i][gclosed] == 1)
            {
                ShowPlayerDialog(playerid, DIALOG_CODE, DIALOG_STYLE_INPUT, "Code", "This door is password protected.\nEnter the code.", "Enter", "");
                GInfo[i][gclosed] = 0;
                GInfo[i][gopening] = 0;
            }
            else
            {
                MoveDynamicObject(GInfo[i][gobject], GInfo[i][gclosedx], GInfo[i][gclosedy], GInfo[i][gclosedz], 3.0, GInfo[i][gclosedrx], GInfo[i][gclosedry], GInfo[i][gclosedrz]);
                GInfo[i][gclosed] = 1;
            }
        }
    }
    return 1;
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    if(dialogid == DIALOG_CODE)
    {
        if(response)
        {
            for(new i=0; i<MAX_GATES; i++)
            {
                if(strmatch(inputtext, GInfo[i][gcode]))
                {
                    MoveDynamicObject(GInfo[i][gobject], GInfo[i][gopenx], GInfo[i][gopeny], GInfo[i][gopenz], 3.0, GInfo[i][gopenrx], GInfo[i][gopenry], GInfo[i][gopenrz]);
                    GInfo[i][gclosed] = 0;
                    GInfo[i][gopening] = 0;
                }
            }
        }
    }
    return 0;
}

stock CreateMovingObject(modelid, Float:closedx, Float:closedy, Float:closedz, Float:closedrx, Float:closedry, Float:closedrz, Float:openx, Float:openy, Float:openz, Float:openrx, Float:openry, Float:openrz, vw, code[6])
{
    gatescreated ++;
    new id = gatescreated;
    GInfo[id][gmodel] = modelid;
    GInfo[id][gclosedx] = closedx;
    GInfo[id][gclosedy] = closedy;
    GInfo[id][gclosedz] = closedz;
    GInfo[id][gclosedrx] = closedrx;
    GInfo[id][gclosedry] = closedry;
    GInfo[id][gclosedrz] = closedrz;
    GInfo[id][gopenx] = openx;
    GInfo[id][gopeny] = openy;
    GInfo[id][gopenz] = openz;
    GInfo[id][gopenrx] = openrx;
    GInfo[id][gopenry] = openry;
    GInfo[id][gopenrz] = openrz;
    GInfo[id][gvw] = vw;
    GInfo[id][gcode] = code;
    return 1;
}

stock strmatch(const String1[], const String2[])//credits to cameltoe
{
    if ((strcmp(String1, String2, true, strlen(String2)) == 0) && (strlen(String2) == strlen(String1)))
    {
        return true;
    }
    else
    {
        return false;
    }
}
The final step is actually adding the gate to your script. The easiest way is to create 2 objects (one open, one closed) and copy the coordinates.

pawn Код:
CreateMovingObject(modelid, Float:closedx, Float:closedy, Float:closedz, Float:closedrx, Float:closedry, Float:closedrz, Float:openx, Float:openy, Float:openz, Float:openrx, Float:openry, Float:openrz, vw, code[6]);
Simply, fill in the parameters with the information from the two objects you created. vw stands for virtual world. If the gate is going to be outside, then set vw to 0. The code, is what is required to open the gate. When you type /gate, you will be prompted to open it. You can have a maximum of 6 characters in your code.
Reply
#2

Good job, +rep. Looks like you worked hard.
Reply
#3

Nice But if you add more gates you ned new command ?
like /gate1 ?
Reply
#4

Quote:
Originally Posted by fredelfredel
Посмотреть сообщение
Nice But if you add more gates you ned new command ?
like /gate1 ?
No, every gate is automatically under the command /gate. When you type /gate a dialog will appear prompting for the password (which you set in the code param)
Reply
#5

E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : error 029: invalid expression, assumed zero
E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : error 029: invalid expression, assumed zero
E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : warning 215: expression has no effect
E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : error 001: expected token: ";", but found "]"
E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : fatal error 107: too many error messages on one line


line 42
CreateMovingObject(16098, 302.12622, 2494.89478, 11.54253, 356.85840, 0.00000, -1.57080, 302.12622, 2494.89478, 20.31014, 356.85840, 0.00000, -1.57080, 0, 444444[6]);


some help im not seee the problem
Reply
#6

Great job. its really awesome, i needed this on my server
Reply
#7

Quote:
Originally Posted by fredelfredel
Посмотреть сообщение
E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : error 029: invalid expression, assumed zero
E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : error 029: invalid expression, assumed zero
E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : warning 215: expression has no effect
E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : error 001: expected token: ";", but found "]"
E:\Samp-Servare+Date\newgm\filterscripts\G1.pwn(42) : fatal error 107: too many error messages on one line


line 42
CreateMovingObject(16098, 302.12622, 2494.89478, 11.54253, 356.85840, 0.00000, -1.57080, 302.12622, 2494.89478, 20.31014, 356.85840, 0.00000, -1.57080, 0, 444444[6]);


some help im not seee the problem
remove the [6] beside the 444444
Reply
#8

Be aware that the OnFilterScriptExit clause will destroy ALL objects across all scripts, not just the objects created by the current script.
Reply
#9

now i got 1 error 035: argument type mismatch (argument 15)
same line 42
Reply
#10

Quote:
Originally Posted by Vince
Посмотреть сообщение
Be aware that the OnFilterScriptExit clause will destroy ALL objects across all scripts, not just the objects created by the current script.
Furthermore it should be noted unloading a filterscript will automatically destroy any dynamic elements created objects, text3d etc there is absolutely no need for any clean up routines.

Another issue is your using IsPlayerInRangeOfPoint() why bother when you can use dynamic areas might as well utilize everything you can from the streamer right.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)