[Tutorial] How to create custom destroyable objects or objects which can get damaged <0.3z>
#1

Creating custom destroyable objects or objects which can get damaged
0.3z

Introduction


Hello everyone,
SA-MP 0.3z R version have been released recently. It provides some cool features like better sync and detects if a player shoots another player, vehicle or even an object. Well as it provides object, it's simply easy to create a system like damaging objects. 0.3z provides the best feature in object destruction because it easily detects the sizes. Most of them detects a range when the player shoots at a position, but on 0.3z, it does the coverage of the object completely and is much efficient!

NOTE: I know that SA-MP 0.3z is still a RC version, however this tutorial is also a concept in regarding damaging objects. I will be updating this tutorial in case if any changes are being occurred on RC updates or R release. SA-MP 0.3z R1 has been released!

NOTE 2: The current tutorial will be explaining in regarding damaging or destroying objects which are either created by "CreateObject" or "CreatePlayerObject" only. About streamer, it will be updated later. I need to test streamer related objects before explaining it here so as to avoid explaining shits.
Streamer has been updated to 0.3z version! - So, streamer tutorial is also found at last.


Requirements


• SA-MP 0.3z server files <Includes, executable files and stuff>
• Streamer 0.3z version (In case if you want this to get performed on streamer's dynamic objects)


Concept


The concept in this tutorial is like shooting down the objects and it gets damaged. Destroying objects directly would not be fair enough because it would be like no good logic and also performance related issues would occur. If you want the object ids to get called while shooting them, it must not be a GTA SA default created object. You must implement it via SA-MP functions.


Tutorial


Let's start the tutorial. First of all, open pawno.exe and include a_samp. It's not actually needed in explaining how to include as this one is relating only to destroy or damage custom objects.
pawn Код:
#include <a_samp>
If you're too much curious to do this, let's start with doing it on a quicker way which will damage every objects which are either player-objects or global ones.

• Creating an HP variable for every objects.

Just like we declare HP for players, we're also declaring our own server-sided HP for objects.
pawn Код:
new ObjectHP[MAX_OBJECTS];
//We've declared a variable called 'ObjectHP' for 1000 objects. (MAX_OBJECTS = 1000)
Now, let's create some objects and assign their HP. In this tutorial, I'm gonna show it as executing these as a filterscript. Many include does hooking purposes, so I suggest you to define FILTERSCRIPT before any includes.

pawn Код:
#define FILTERSCRIPT

#include <a_samp>
• Creating objects

Creating objects is pretty simple, you can use the function called CreateObject. Or, more easier by using SA-MP map editor or any other map editors. I suggest SA-MP map editor as it's more convenient and faster. Here, I'm considering that I've created 5 objects on Los Santos Airport.

NOTE: Always prefer using objects which don't get destroyed by default. There's many objects which gets destroyed as it's set default. Either get them to be destroyed when player shoots them at first itself.

Before implementing those CreateObject codes under OnFilterScriptInit, it's necessary to reset every object's HP. And then, after creating, set them back. On the quicker way, I'm letting every "CreateObject" objects to face damages when a player shoots it.

pawn Код:
#define FILTERSCRIPT

#include <a_samp>

new ObjectHP[MAX_OBJECTS];

public OnFilterScriptInit()
{
    //Let's loop through MAX_OBJECTS and reset their HP.
    for(new i; i< MAX_OBJECTS; i++) ObjectHP[i] = 0;
    /*What the above code does is, it loops till 1000 as MAX_OBJECT's value is 1000.
    Basically it does:
    ObjectHP[0] = 0;
    ObjectHP[1] = 0;
    ObjectHP[2] = 0;
    ...
    ObjectHP[999] = 0; */

    CreateObject(18848, 1450.59, -2446.57, 12.50,   0.00, 0.00, 0.00);
    CreateObject(18848, 1442.11, -2444.87, 12.55,   0.00, 0.00, 0.00);
    CreateObject(18848, 1461.18, -2446.89, 12.54,   0.00, 0.00, 0.00);
    CreateObject(18848, 1457.31, -2430.67, 12.54,   0.00, 0.00, 0.00);
    CreateObject(18848, 1449.48, -2430.51, 12.54,   0.00, 0.00, 0.00);
    //I've created 5 objects which are S-A-M missile launchers (Unbreakable)
    //Now, let me set their HP.
    for(new i; i< MAX_OBJECTS; i++) { //Looping once again
        if(!IsValidObject(i)) continue; /*If it's not a valid object, it skips that loop and jumps to next.
        ! being used before this function means if it returns a false statement.
        */

        ObjectHP[i] = 100; //I'm setting every valid object's HP to 100 as this is a quicker way.
    }
    return 1;
}
NOTE: This is a quick way method, so it will set every valid CreateObject coded objects to have 100% HP.

So, we've done with creating objects and setting their HP rates.

• Damaging the objects

Since 0.3z, shooting an object always calls "OnPlayerWeaponShot". And since 0.3z R2, it supports an easier method in player-objects also getting called under the same callback. On this quick method, I'm just explaining about normal global objects.

OnPlayerWeaponShot gets called accordingly :
pawn Код:
public OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ)
{
 return 1;
playerid - The player who shoots.
weaponid - The weapon's ID in which player used for shooting.
hittype - The type of hit occurred.
There's 4 types of hit.
pawn Код:
#define BULLET_HIT_TYPE_NONE                0
 #define BULLET_HIT_TYPE_PLAYER              1
 #define BULLET_HIT_TYPE_VEHICLE             2
 #define BULLET_HIT_TYPE_OBJECT              3
 #define BULLET_HIT_TYPE_PLAYER_OBJECT   4

BULLET_HIT_TYPE_NONE
gets called on this callback if player shoots nothing.
BULLET_HIT_TYPE_PLAYER gets called on this callback in case if player shoots another player.
BULLET_HIT_TYPE_VEHICLE gets called in case if player shoots a vehicle.
BULLET_HIT_TYPE_OBJECT gets called in case if player shoots an object and finally
BULLET_HIT_TYPE_PLAYER_OBJECT gets called in case if player shoots a player-object.

hitid
hitid is the ID in relating to the hittype. In case if hittype goes TYPE_PLAYER, it returns the player ID as hitid. Or if it's a vehicle, it returns the vehicle's ID as hitid. Same facts on object and player-objects; it will return the object ID or player object ID in case if the hittype is either TYPE_OBJECT or TYPE_PLAYER_OBJECT.

fX, fY, fZ
These are the offsets within the object ID, vehicle ID or the player's skin. Which means the position where the bullet gets hit. Not actually the POS, but the offset position of an object, player-object, vehicle or a player's skin.

We're gonna use this callback to reduce the HP of object and destroy it.
pawn Код:
public OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ)
{
    //We must know if the bullet hit type is related to object, so use an if statement and verify if it's hitting an object.
    if(hittype == BULLET_HIT_TYPE_OBJECT) { //In case if player hits an object
        if(IsValidObject(hitid)) { /*The callback may get called only if hitting valid objects, but still to verify much,
        we can know if it's a valid object or not using 'IsValidObject'. Here, in case if it's a valid object, then:
        We're gonna reduce it's HP. Reducing a custom value wouldn't seem to be good, so let's randomize the value.
        Reducing also depends on the firerates, so if player got good firerates, reducing would go fastly.
        */

            ObjectHP[hitid] -= random(10) + 1;
            /*
            Here, we're reducing the ObjectHP of the object ID which is called on this callback. It reduces from a
            minimum of 1. random(max) means it will return any numbers starting from random to the max specified value.
            I've added additional +1 to it because in case if 0 is the result, it sums up and doesn't result in 0.

            Now, in case if object's HP goes at 0 or lower than that, we must destroy it. */

            if(ObjectHP[hitid] <= 0) { //If objectHP of that object is either 0 or lower,:
                DestroyObject(hitid);
            }
        }
    }
    return 1;
}
No, we're not done. We've just finished on the concept of damaging objects and destroying them. To make it look much better, we can give some information to player in regarding the object's HP. This is not necessary, the concept is what required. The below code is just in showing object information to player after damaging it.

pawn Код:
public OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ)
{
    //We must know if the bullet hit type is related to object, so use an if statement and verify if it's hitting an object.
    if(hittype == BULLET_HIT_TYPE_OBJECT) { //In case if player hits an object
        if(IsValidObject(hitid)) { /*The callback may get called only if hitting valid objects, but still to verify much,
        we can know if it's a valid object or not using 'IsValidObject'. Here, in case if it's a valid object, then:
        We're gonna reduce it's HP. Reducing a custom value wouldn't seem to be good, so let's randomize the value.
        Reducing also depends on the firerates, so if player got good firerates, reducing would go fastly.
        */

            ObjectHP[hitid] -= random(10) + 1;
            /*
            Here, we're reducing the ObjectHP of the object ID which is called on this callback. It reduces from a
            minimum of 1. random(max) means it will return any numbers starting from random to the max specified value.
            I've added additional +1 to it because in case if 0 is the result, it sums up and doesn't result in 0.

            Now, in case if object's HP goes at 0 or lower than that, we must destroy it. */

            if(ObjectHP[hitid] >= 0) { //If object's HP is either 0 or 1.
                new str[128]; //We will need to declare a string to display the message.
                //I'll be using GameTextForPlayer because if we're using SendClientMessage, it could always spam.
                format(str, sizeof(str), "~G~OBJECT DAMAGED!~N~~Y~OBJECT ID ~W~: ~R~ %d~N~~Y~HP LEFT ~W~: ~R~ %d", hitid, ObjectHP[hitid]);
                //I've formatted the string which includes the information of object id and it's HP left.
                GameTextForPlayer(playerid, str, 1800, 3);
                //I'm displaying the info to player. I'm using style '3' because it's the better one in case if this callback is being called at many times.
            }
            if(ObjectHP[hitid] <= 0) { //If objectHP of that object is either 0 or lower,:
                DestroyObject(hitid);
            }
        }
    }
    return 1;
}
• Complete result with the above mentioned

pawn Код:
#define FILTERSCRIPT

#include <a_samp>

new ObjectHP[MAX_OBJECTS];

public OnFilterScriptInit()
{
    //Let's loop through MAX_OBJECTS and reset their HP.
    for(new i; i< MAX_OBJECTS; i++) ObjectHP[i] = 0;
    /*What the above code does is, it loops till 1000 as MAX_OBJECT's value is 1000.
    Basically it does:
    ObjectHP[0] = 0;
    ObjectHP[1] = 0;
    ObjectHP[2] = 0;
    ...
    ObjectHP[999] = 0; */

    CreateObject(18848, 1450.59, -2446.57, 12.50,   0.00, 0.00, 0.00);
    CreateObject(18848, 1442.11, -2444.87, 12.55,   0.00, 0.00, 0.00);
    CreateObject(18848, 1461.18, -2446.89, 12.54,   0.00, 0.00, 0.00);
    CreateObject(18848, 1457.31, -2430.67, 12.54,   0.00, 0.00, 0.00);
    CreateObject(18848, 1449.48, -2430.51, 12.54,   0.00, 0.00, 0.00);
    //I've created 5 objects which are S-A-M missile launchers (Unbreakable)
    //Now, let me set their HP.
    for(new i; i< MAX_OBJECTS; i++) { //Looping once again
        if(!IsValidObject(i)) continue; /*If it's not a valid object, it skips that loop and jumps to next.
        ! being used before this function means if it returns a false statement.
        */

        ObjectHP[i] = 100; //I'm setting every valid object's HP to 100 as this is a quicker way.
    }
    return 1;
}

public OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ)
{
    //We must know if the bullet hit type is related to object, so use an if statement and verify if it's hitting an object.
    if(hittype == BULLET_HIT_TYPE_OBJECT) { //In case if player hits an object
        if(IsValidObject(hitid)) { /*The callback may get called only if hitting valid objects, but still to verify much,
        we can know if it's a valid object or not using 'IsValidObject'. Here, in case if it's a valid object, then:
        We're gonna reduce it's HP. Reducing a custom value wouldn't seem to be good, so let's randomize the value.
        Reducing also depends on the firerates, so if player got good firerates, reducing would go fastly.
        */

            ObjectHP[hitid] -= random(10) + 1;
            /*
            Here, we're reducing the ObjectHP of the object ID which is called on this callback. It reduces from a
            minimum of 1. random(max) means it will return any numbers starting from random to the max specified value.
            I've added additional +1 to it because in case if 0 is the result, it sums up and doesn't result in 0.

            Now, in case if object's HP goes at 0 or lower than that, we must destroy it. */

            if(ObjectHP[hitid] >= 0) { //If object's HP is either 0 or 1.
                new str[128]; //We will need to declare a string to display the message.
                //I'll be using GameTextForPlayer because if we're using SendClientMessage, it could always spam.
                format(str, sizeof(str), "~G~OBJECT DAMAGED!~N~~Y~OBJECT ID ~W~: ~R~ %d~N~~Y~HP LEFT ~W~: ~R~ %d", hitid, ObjectHP[hitid]);
                //I've formatted the string which includes the information of object id and it's HP left.
                GameTextForPlayer(playerid, str, 1800, 3);
                //I'm displaying the info to player. I'm using style '3' because it's the better one in case if this callback is being called at many times.
            }
            if(ObjectHP[hitid] <= 0) { //If objectHP of that object is either 0 or lower,:
                DestroyObject(hitid);
            }
        }
    }
    return 1;
}
Well and that's it with damaging objects and destroying them in an easier way. The next step is the advanced method which includes player-objects too. Not actually advanced, but in a way you can group objects which could face damage.

• Grouped method

In grouped method, let's group objects which needs to get damaged. So it will get damaged only if they're assigned to receive damages. Just like mentioned on the Quicker method, you must first include and declare the ObjectHP variable.

pawn Код:
#define FILTERSCRIPT
//This example is also a FS related example.

#include <a_samp>

new ObjectHP[MAX_OBJECTS];
Now, let's declare another variable to check if that object is assigned to receive damage or not. This will allow you to damage those which are assigned to be damaged only. So the rest ones won't get damaged even if the callback gets called.

pawn Код:
new bool:ObjectDamage[MAX_OBJECTS]; //Let's create this as a boolean as there's only two statements to be declared.
//I'll be using it in a way as if the variable is true, it means the object is assigned to face damage, else not.
Just like it's done on quicker method, I'm creating objects. But here, I'm retrieving the object IDs and assigning those object IDs to true on the variable ObjectDamage. This is being done because those objects will be then able to face damage where as the others won't.

pawn Код:
#define FILTERSCRIPT

#include <a_samp>

new ObjectHP[MAX_OBJECTS],
    bool:ObjectDamage[MAX_OBJECTS];

public OnFilterScriptInit()
{
    //Let's loop through MAX_OBJECTS and reset their HP.
    for(new i; i< MAX_OBJECTS; i++) {
        ObjectHP[i] = 0;
        /*What the above code does is, it loops till 1000 as MAX_OBJECT's value is 1000.
        Basically it does:
        ObjectHP[0] = 0;
        ObjectHP[1] = 0;
        ObjectHP[2] = 0;
        ...
        ObjectHP[999] = 0; */

       
        //On the grouped method, I'm also resetting the assigned objects.
        ObjectDamage[i] = false;
        /* This performs in a loop in such a way that :
        ObjectDamage[0] = false;
        ObjectDamage[1] = false;
        ObjectDamage[2] = false;
        ...
        ObjectDamage[3] = false; */

    }
    /*
    Now, these are the objects which requires to be assigned to face damage.
    I'll retrieve the object IDs and assign those IDs to face damage.
    I'm declaring an array to retrieve the object IDs.*/

    new Object_ID[5]; //There's 5 objects, so size of 5.
    Object_ID[0] = CreateObject(18848, 1450.59, -2446.57, 12.50,   0.00, 0.00, 0.00);
    Object_ID[1] = CreateObject(18848, 1442.11, -2444.87, 12.55,   0.00, 0.00, 0.00);
    Object_ID[2] = CreateObject(18848, 1461.18, -2446.89, 12.54,   0.00, 0.00, 0.00);
    Object_ID[3] = CreateObject(18848, 1457.31, -2430.67, 12.54,   0.00, 0.00, 0.00);
    Object_ID[4] = CreateObject(18848, 1449.48, -2430.51, 12.54,   0.00, 0.00, 0.00);
    //I've created 5 objects which are S-A-M missile launchers (Unbreakable) and also grouped.
    //Now, let me set their HP and assign them.
    //This time, we'll set the HP only to the assigned ones.
    //First of all, let me assign the ones.
    for(new i; i< 5; i++) {
        ObjectDamage[Object_ID[i]] = true; //The ObjectDamage boolean sets true to the object ids which have been retrieved.
        /*Under loop, it does more faster and does in a way like this :
        ObjectDamage[Object_ID[0]] = true;
        ObjectDamage[Object_ID[1]] = true;
        ...
        ObjectDamage[Object_ID[4]] = true; */

    }
    //Now just like I did before on the earlier method, I'm setting object's HP.
    //But this time,I'm setting the HP of valid and assigned objects only.
    for(new i; i< MAX_OBJECTS; i++) {
        if(!IsValidObject(i)) continue; //Skips if it's not a valid object.
        if(ObjectDamage[i] == false) continue; //If they're not assigned or not set to true, it will skip.
        ObjectHP[i] = 100; //You can also change the HP rate according to the model ID of objects.
    }
    return 1;
}
I've got grouped objects, now we've gotta do the damage functioning for grouped ones. It's pretty simple for you now if you've read the quicker method.

pawn Код:
public OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ)
{
    //We must know if the bullet hit type is related to object, so use an if statement and verify if it's hitting an object.
    if(hittype == BULLET_HIT_TYPE_OBJECT) { //In case if player hits an object
        if(IsValidObject(hitid)) { /*The callback may get called only if hitting valid objects, but still to verify much,
        we can know if it's a valid object or not using 'IsValidObject'. Here, in case if it's a valid object, then:
        We're gonna reduce it's HP. Reducing a custom value wouldn't seem to be good, so let's randomize the value.
        Reducing also depends on the firerates, so if player got good firerates, reducing would go fastly.
        */

       
        //This time, we're also looking if the object ID is assigned to true, else it won't get damaged!
            if(ObjectDamage[hitid] == true) { //In case if it's assigned or true, it will damage.
           
                ObjectHP[hitid] -= random(10) + 1;
                /*
                Here, we're reducing the ObjectHP of the object ID which is called on this callback. It reduces from a
                minimum of 1. random(max) means it will return any numbers starting from random to the max specified value.
                I've added additional +1 to it because in case if 0 is the result, it sums up and doesn't result in 0.

                Now, in case if object's HP goes at 0 or lower than that, we must destroy it. */

                if(ObjectHP[hitid] >= 0) { //If object's HP is either 0 or 1.
                    new str[128]; //We will need to declare a string to display the message.
                    //I'll be using GameTextForPlayer because if we're using SendClientMessage, it could always spam.
                    format(str, sizeof(str), "~G~OBJECT DAMAGED!~N~~Y~OBJECT ID ~W~: ~R~ %d~N~~Y~HP LEFT ~W~: ~R~ %d", hitid, ObjectHP[hitid]);
                    //I've formatted the string which includes the information of object id and it's HP left.
                    GameTextForPlayer(playerid, str, 1800, 3);
                    //I'm displaying the info to player. I'm using style '3' because it's the better one in case if this callback is being called at many times.
                }
                if(ObjectHP[hitid] <= 0) { //If objectHP of that object is either 0 or lower,:
                    DestroyObject(hitid);
                }
            }
           
        }
    }
    return 1;
}
If you're looking for an example which damages the assigned ones but not the one which isn't assigned, here's it. Test it for yourself!

pawn Код:
#define FILTERSCRIPT

#include <a_samp>

new ObjectHP[MAX_OBJECTS],
    bool:ObjectDamage[MAX_OBJECTS];

public OnFilterScriptInit()
{
    //Let's loop through MAX_OBJECTS and reset their HP.
    for(new i; i< MAX_OBJECTS; i++) {
        ObjectHP[i] = 0;
        /*What the above code does is, it loops till 1000 as MAX_OBJECT's value is 1000.
        Basically it does:
        ObjectHP[0] = 0;
        ObjectHP[1] = 0;
        ObjectHP[2] = 0;
        ...
        ObjectHP[999] = 0; */

       
        //On the grouped method, I'm also resetting the assigned objects.
        ObjectDamage[i] = false;
        /* This performs in a loop in such a way that :
        ObjectDamage[0] = false;
        ObjectDamage[1] = false;
        ObjectDamage[2] = false;
        ...
        ObjectDamage[3] = false; */

    }
    /*
    Now, these are the objects which requires to be assigned to face damage.
    I'll retrieve the object IDs and assign those IDs to face damage.
    I'm declaring an array to retrieve the object IDs.*/

    new Object_ID[5]; //There's 5 objects, so size of 5.
    Object_ID[0] = CreateObject(18848, 1450.59, -2446.57, 12.50,   0.00, 0.00, 0.00);
    Object_ID[1] = CreateObject(18848, 1442.11, -2444.87, 12.55,   0.00, 0.00, 0.00);
    Object_ID[2] = CreateObject(18848, 1461.18, -2446.89, 12.54,   0.00, 0.00, 0.00);
    Object_ID[3] = CreateObject(18848, 1457.31, -2430.67, 12.54,   0.00, 0.00, 0.00);
    Object_ID[4] = CreateObject(18848, 1449.48, -2430.51, 12.54,   0.00, 0.00, 0.00);
    CreateObject(18848, 1486.46, -2439.21, 12.50,   0.00, 0.00, 0.00); //A normal object which isn't assigned to face damage.
    //I've created 5 objects which are S-A-M missile launchers (Unbreakable) and also grouped.
    //Now, let me set their HP and assign them.
    //This time, we'll set the HP only to the assigned ones.
    //First of all, let me assign the ones.
    for(new i; i< 5; i++) {
        ObjectDamage[Object_ID[i]] = true; //The ObjectDamage boolean sets true to the object ids which have been retrieved.
        /*Under loop, it does more faster and does in a way like this :
        ObjectDamage[Object_ID[0]] = true;
        ObjectDamage[Object_ID[1]] = true;
        ...
        ObjectDamage[Object_ID[4]] = true; */

    }
    //Now just like I did before on the earlier method, I'm setting object's HP.
    //But this time,I'm setting the HP of valid and assigned objects only.
    for(new i; i< MAX_OBJECTS; i++) {
        if(!IsValidObject(i)) continue; //Skips if it's not a valid object.
        if(ObjectDamage[i] == false) continue; //If they're not assigned or not set to true, it will skip.
        ObjectHP[i] = 100; //You can also change the HP rate according to the model ID of objects.
    }
    return 1;
}

public OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ)
{
    //We must know if the bullet hit type is related to object, so use an if statement and verify if it's hitting an object.
    if(hittype == BULLET_HIT_TYPE_OBJECT) { //In case if player hits an object
        if(IsValidObject(hitid)) { /*The callback may get called only if hitting valid objects, but still to verify much,
        we can know if it's a valid object or not using 'IsValidObject'. Here, in case if it's a valid object, then:
        We're gonna reduce it's HP. Reducing a custom value wouldn't seem to be good, so let's randomize the value.
        Reducing also depends on the firerates, so if player got good firerates, reducing would go fastly.
        */

       
        //This time, we're also looking if the object ID is assigned to true, else it won't get damaged!
            if(ObjectDamage[hitid] == true) { //In case if it's assigned or true, it will damage.
           
                ObjectHP[hitid] -= random(10) + 1;
                /*
                Here, we're reducing the ObjectHP of the object ID which is called on this callback. It reduces from a
                minimum of 1. random(max) means it will return any numbers starting from random to the max specified value.
                I've added additional +1 to it because in case if 0 is the result, it sums up and doesn't result in 0.

                Now, in case if object's HP goes at 0 or lower than that, we must destroy it. */

                if(ObjectHP[hitid] >= 0) { //If object's HP is either 0 or 1.
                    new str[128]; //We will need to declare a string to display the message.
                    //I'll be using GameTextForPlayer because if we're using SendClientMessage, it could always spam.
                    format(str, sizeof(str), "~G~OBJECT DAMAGED!~N~~Y~OBJECT ID ~W~: ~R~ %d~N~~Y~HP LEFT ~W~: ~R~ %d", hitid, ObjectHP[hitid]);
                    //I've formatted the string which includes the information of object id and it's HP left.
                    GameTextForPlayer(playerid, str, 1800, 3);
                    //I'm displaying the info to player. I'm using style '3' because it's the better one in case if this callback is being called at many times.
                }
                if(ObjectHP[hitid] <= 0) { //If objectHP of that object is either 0 or lower,:
                    DestroyObject(hitid);
                }
            }
           
        }
    }
    return 1;
}

public OnPlayerCommandText(playerid, cmdtext[])
{
    if(!strcmp(cmdtext, "/ls")) return SetPlayerPos(playerid,1450.59, -2446.57, 12.50+5.0);
    return 0;
}
On the above code, there's a total of 6 SAM launchers. 5 of them are assigned to face damage, the one which is a bit more far doesn't get damaged as it's not assigned to get damaged.

• Per-Player Objects

NOTE: You should've read the above parts to know more about player object related damaging more. Because I've explained more on those parts only!


Per-Player Objects are the ones which are visible only to the player in which it's specified. We're using the function CreatePlayerObject to create a player object. On the WIKI page it's being created under OnPlayerConnect but I suggest to use OnPlayerSpawn to create player objects and OnPlayerConnect isn't that efficient for player objects getting created.

On this, create the variables like mentioned above, either grouped or non-grouped ones. Here, I'm doing grouped ones.

pawn Код:
#define FILTERSCRIPT

#include <a_samp>

new PlayerObjectHP[MAX_PLAYERS][MAX_OBJECTS];
//It's a player object, so it needs an extra slot for storing player ID too. And hence, it's a 2D array.

new bool:PlayerObjectDamage[MAX_PLAYERS][MAX_OBJECTS];
//Just like I declared PlayerObjectHP,  I'm declaring the group assigning variable too.
I've mentioned that we're gonna create player-objects under OnPlayerSpawn, so let's get started in creating objects under the onplayerspawn.

pawn Код:
#define FILTERSCRIPT

#include <a_samp>

new PlayerObjectHP[MAX_PLAYERS][MAX_OBJECTS];
//It's a player object, so it needs an extra slot for storing player ID too. And hence, it's a 2D array.

new bool:PlayerObjectDamage[MAX_PLAYERS][MAX_OBJECTS];
//Just like I declared PlayerObjectHP,  I'm declaring the group assigning variable too.

new p_OBJECTS[MAX_PLAYERS][5]; //Another 2D array to retrieve player's OBJECT ID. This is also used to destroy the object in case if it's alive when player disconnects.

new p_NODAMAGE[MAX_PLAYERS]; //This variable is to store the object which don't face damages. Why I'm declaring this is because I need to destroy this when player leaves.

public OnPlayerSpawn(playerid)
{
    p_OBJECTS[playerid][0] = CreatePlayerObject(playerid, 18848, 1450.59, -2446.57, 12.50,   0.00, 0.00, 0.00);
    p_OBJECTS[playerid][1] = CreatePlayerObject(playerid, 18848, 1442.11, -2444.87, 12.55,   0.00, 0.00, 0.00);
    p_OBJECTS[playerid][2] = CreatePlayerObject(playerid, 18848, 1461.18, -2446.89, 12.54,   0.00, 0.00, 0.00);
    p_OBJECTS[playerid][3] = CreatePlayerObject(playerid, 18848, 1457.31, -2430.67, 12.54,   0.00, 0.00, 0.00);
    p_OBJECTS[playerid][4] = CreatePlayerObject(playerid, 18848, 1449.48, -2430.51, 12.54,   0.00, 0.00, 0.00);
    p_NODAMAGE[playerid] = CreatePlayerObject(playerid, 18848, 1486.46, -2439.21, 12.50,   0.00, 0.00, 0.00);
    //Now I've retrieved these created object's ID on these variables.
    //The ones stored on p_OBJECTS are to be assigned to get damaged and the p_NODAMAGE won't get any damages.
    //Just like I've done to global ones, here also I'm doing a loop and assigning these player objects to face damage.
    for(new i; i< 5; i++) {
       PlayerObjectDamage[playerid][p_OBJECTS[playerid][i]] = true; //It sets the assigned player object IDs to true so that it'll receive damages.
    }
    //Another loop in setting the player object's HP.
    for(new i; i< MAX_OBJECTS; i++) {
        if(!IsValidPlayerObject(playerid, i)) continue;
        /* IsValidPlayerObject checks if it's a valid player-object for the specified player ID. I'm skipping the -
        loop value in case if it's returning false.
        */

        PlayerObjectHP[playerid][i] = 100; //Sets the player-object's HP to 100.
    }
    return 1;
}
I've been using BULLET_HIT_TYPE_OBJECT under "OnPlayerWeaponShot" to detect global objects. Here, I'm going to use BULLET_HIT_TYPE_PLAYER_OBJECT as I'm gonna detect player-objects.

pawn Код:
public OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ)
{
    if(hittype == BULLET_HIT_TYPE_PLAYER_OBJECT) { //In case if player is shooting at a player object.
        if(IsValidPlayerObject(playerid, hitid)) { //If the called player-object is valid for the player, then:
            if(PlayerObjectDamage[playerid][hitid] == true) { //If the player's object ID is assigned to take damage, then:
                //Now just like I've done to global objects, I'm once again reducing these object's HP randomly.
                PlayerObjectHP[playerid][hitid] -= random(10) + 1;
                if(PlayerObjectHP[playerid][hitid] >= 0) { //If the Player's Object's HP is either 0 or more;
                    new str[128]; //This concept isn't actually needed, however it's to show info of the damage.
                    //And yes, we're declaring a string variable to format it with the specifiers.
                    format(str, sizeof(str), "~G~DAMAGE DONE!~N~~Y~PLAYER OBJECT ID ~W~: ~R~ %d~N~~Y~HP LEFT ~W~: ~R~ %d", hitid, PlayerObjectHP[playerid][hitid]);
                    GameTextForPlayer(playerid, str, 1800, 3);
                }
                if(PlayerObjectHP[playerid][hitid] <= 0) { //If the HP goes to 0 or goes lesser than 0;
                    DestroyPlayerObject(playerid, hitid); //We're destroying the PLAYER's OBJECT then, not a global one.
                }
            }
        }
    }
    return 1;
}

We aren't yet done with player objects. These have to be destroyed under OnPlayerDisconnect if player hasn't destroyed them yet!

pawn Код:
public OnPlayerDisconnect(playerid, reason)
{
    //We'll loop through every objects at first.
    for(new i; i< MAX_OBJECTS; i++) {
        //Then, we'll see if it's a player object or not.
        if(!IsValidPlayerObject(playerid, i)) continue; //If it's not a valid player object, it will skip that loop value.
        DestroyPlayerObject(playerid, i); //In case if there's any player objects, it will be destroying them.
    }
    return 1;
}
• Complete codes of per-player object damaging

pawn Код:
#define FILTERSCRIPT

#include <a_samp>

new PlayerObjectHP[MAX_PLAYERS][MAX_OBJECTS];
//It's a player object, so it needs an extra slot for storing player ID too. And hence, it's a 2D array.

new bool:PlayerObjectDamage[MAX_PLAYERS][MAX_OBJECTS];
//Just like I declared PlayerObjectHP,  I'm declaring the group assigning variable too.

new p_OBJECTS[MAX_PLAYERS][5]; //Another 2D array to retrieve player's OBJECT ID. This is also used to destroy the object in case if it's alive when player disconnects.

new p_NODAMAGE[MAX_PLAYERS]; //This variable is to store the object which don't face damages. Why I'm declaring this is because I need to destroy this when player leaves.

public OnPlayerSpawn(playerid)
{
    p_OBJECTS[playerid][0] = CreatePlayerObject(playerid, 18848, 1450.59, -2446.57, 12.50,   0.00, 0.00, 0.00);
    p_OBJECTS[playerid][1] = CreatePlayerObject(playerid, 18848, 1442.11, -2444.87, 12.55,   0.00, 0.00, 0.00);
    p_OBJECTS[playerid][2] = CreatePlayerObject(playerid, 18848, 1461.18, -2446.89, 12.54,   0.00, 0.00, 0.00);
    p_OBJECTS[playerid][3] = CreatePlayerObject(playerid, 18848, 1457.31, -2430.67, 12.54,   0.00, 0.00, 0.00);
    p_OBJECTS[playerid][4] = CreatePlayerObject(playerid, 18848, 1449.48, -2430.51, 12.54,   0.00, 0.00, 0.00);
    p_NODAMAGE[playerid] = CreatePlayerObject(playerid, 18848, 1486.46, -2439.21, 12.50,   0.00, 0.00, 0.00);
    //Now I've retrieved these created object's ID on these variables.
    //The ones stored on p_OBJECTS are to be assigned to get damaged and the p_NODAMAGE won't get any damages.
    //Just like I've done to global ones, here also I'm doing a loop and assigning these player objects to face damage.
    for(new i; i< 5; i++) {
       PlayerObjectDamage[playerid][p_OBJECTS[playerid][i]] = true; //It sets the assigned player object IDs to true so that it'll receive damages.
    }
    //Another loop in setting the player object's HP.
    for(new i; i< MAX_OBJECTS; i++) {
        if(!IsValidPlayerObject(playerid, i)) continue;
        /* IsValidPlayerObject checks if it's a valid player-object for the specified player ID. I'm skipping the -
        loop value in case if it's returning false.
        */

        PlayerObjectHP[playerid][i] = 100; //Sets the player-object's HP to 100.
    }
    return 1;
}

public OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ)
{
    if(hittype == BULLET_HIT_TYPE_PLAYER_OBJECT) { //In case if player is shooting at a player object.
        if(IsValidPlayerObject(playerid, hitid)) { //If the called player-object is valid for the player, then:
            if(PlayerObjectDamage[playerid][hitid] == true) { //If the player's object ID is assigned to take damage, then:
                //Now just like I've done to global objects, I'm once again reducing these object's HP randomly.
                PlayerObjectHP[playerid][hitid] -= random(10) + 1;
                if(PlayerObjectHP[playerid][hitid] >= 0) { //If the Player's Object's HP is either 0 or more;
                    new str[128]; //This concept isn't actually needed, however it's to show info of the damage.
                    //And yes, we're declaring a string variable to format it with the specifiers.
                    format(str, sizeof(str), "~G~DAMAGE DONE!~N~~Y~PLAYER OBJECT ID ~W~: ~R~ %d~N~~Y~HP LEFT ~W~: ~R~ %d", hitid, PlayerObjectHP[playerid][hitid]);
                    GameTextForPlayer(playerid, str, 1800, 3);
                }
                if(PlayerObjectHP[playerid][hitid] <= 0) { //If the HP goes to 0 or goes lesser than 0;
                    DestroyPlayerObject(playerid, hitid); //We're destroying the PLAYER's OBJECT then, not a global one.
                }
            }
        }
    }
    return 1;
}

public OnPlayerDisconnect(playerid, reason)
{
    //We'll loop through every objects at first.
    for(new i; i< MAX_OBJECTS; i++) {
        //Then, we'll see if it's a player object or not.
        if(!IsValidPlayerObject(playerid, i)) continue; //If it's not a valid player object, it will skip that loop value.
        DestroyPlayerObject(playerid, i); //In case if there's any player objects, it will be destroying them.
    }
    return 1;
}
• Streamer Objects

This is pretty easy, you could just change "CreateObject" or "CreatePlayerObject" to "CreateDynamicObject". And the "OnPlayerWeaponShot" can be changed to "OnPlayerShootDynamicObject". But here's the tutorial for it:
pawn Код:
public OnPlayerShootDynamicObject(playerid, weaponid, objectid, Float:x, Float:y, Float:z)
{
        //objectid returns the object which has received shot!
        if(IsValidDynamicObject(objectid)) { //Dynamic objects can be known by "IsValidDynamicObject", per-player objects too include these.
            if(PlayerObjectDamage[playerid][objectid] == true) { //If the player's object ID is assigned to take damage, then:
                //Now just like I've done to global objects, I'm once again reducing these object's HP randomly.
                PlayerObjectHP[playerid][objectid] -= random(10) + 1;
                if(PlayerObjectHP[playerid][objectid] >= 0) { //If the Player's Object's HP is either 0 or more;
                    new str[128]; //This concept isn't actually needed, however it's to show info of the damage.
                    //And yes, we're declaring a string variable to format it with the specifiers.
                    format(str, sizeof(str), "~G~DAMAGE DONE!~N~~Y~PLAYER OBJECT ID ~W~: ~R~ %d~N~~Y~HP LEFT ~W~: ~R~ %d", hitid, PlayerObjectHP[playerid][hitid]);
                    GameTextForPlayer(playerid, str, 1800, 3);
                }
                if(PlayerObjectHP[playerid][objectid] <= 0) { //If the HP goes to 0 or goes lesser than 0;
                    DestroyDynamicObject(objectid); //We're destroying the PLAYER's OBJECT then, not a global one.
                }
            }
        }
    }
    return 1;
}
And well I hope that's the end of the tutorial in regarding damaging objects and destroying them. It's pretty simple if you know how to use it. Questions and anything related to the tutorial can be posted here.


Example Script


Here's an example filterscript of destroying object. It's not a release, I've just used it today to test how things work. Feel free to edit it though.

http://pastebin.com/UE7zBUKZ - Thanks to Deadspirit for the Pirate island map.

◘ Video of the above filterscript.

[ame]http://www.youtube.com/watch?v=Vu1hL24dFWc[/ame]

Additional Notes

• OnPlayerWeaponShot may not get called if you're using weapons which doesn't have the normal cross hair aiming.
• In case if you're using a breakable object, I suggest you to use the "OnPlayerWeaponShot" callback and either destroy it when any player shoots at it. If you want to add it again, better delay that and add or else player could just spam shooting them out and the performance would get bad.
• OnPlayerWeaponShot won't get called on objects which are just textures but not solid!
• Use of iterators to hold the objects could improve the performance.
Reply
#2

This is actually awesome tutorial, thanks. REP
Reply
#3

Great work and a very interesting tutorial. +Rep
Reply
#4

Great work man, as always
Reply
#5

Nice tutorial!, and awesome idea.

Would be nice if you make a video to see how it looks.
Reply
#6

Thanks everyone.
@davve95, I've done the video and here's it.

[ame]http://www.youtube.com/watch?v=Vu1hL24dFWc[/ame]
Reply
#7

Quote:
Originally Posted by Lordz™
Посмотреть сообщение
Thanks everyone.
@davve95, I've done the video and here's it.

http://www.youtube.com/watch?v=Vu1hL24dFWc
Thanks!, looks good.

You could create another object on it so it looks damaged.

Or rotate the object so it looks like it felt down of damage.

And/or also a applying texture that looks damaged like a black one so it looks it has been exploded or something etc..

Pretty hard to explain but I hope u get it.
Reply
#8

Quote:
Originally Posted by davve95
Посмотреть сообщение
Thanks!, looks good.

You could create another object on it so it looks damaged.

Or rotate the object so it looks like it felt down of damage.

And/or also a applying texture that looks damaged like a black one so it looks it has been exploded or something etc..

Pretty hard to explain but I hope u get it.
Same here, I've also thought about these. But this is just a tutorial which shows the concept of damaging objects, so I've thought of explaining the concept only. I may later get some good textures which are suited for different models and maybe create a video. :- )
Reply
#9

nice tut
Reply
#10

would be good if particle objects are also being posted and the way to use it
Reply
#11

Could you add a part that you can destroy a certain object, not a whole map?
Reply
#12

Quote:
Originally Posted by [WA]iRonan
View Post
Could you add a part that you can destroy a certain object, not a whole map?
You should have read the whole tutorial, I've explained those on the part "Grouped Objects" which clearly shows that it doesn't destroy a whole map and allows damaging certain ones only.

@BlueSky_,
I will be doing that soon, thank you for suggesting.
Reply
#13

wow this is awesome this can be used to create training objects like in ammu nation in singleplayer!
Rep+
Reply
#14

Quote:
Originally Posted by Battlezone
View Post
wow this is awesome this can be used to create training objects like in ammu nation in singleplayer!
Rep+
Thanks and yeah this can be used for training purposes. I suggest to change player's virtual world and create player objects in shooting practices so that it wouldn't collide with others or else a big room should get created. :- )
Reply
#15

Quote:
Originally Posted by Lordz™
View Post
You should have read the whole tutorial, I've explained those on the part "Grouped Objects" which clearly shows that it doesn't destroy a whole map and allows damaging certain ones only.

@BlueSky_,
I will be doing that soon, thank you for suggesting.
Then I read over it. Nice job though!
Reply
#16

Nice tutorial!
Reply
#17

It's all pretty self-explanatory. But still, nice tutorial.
Reply
#18

Quote:
Originally Posted by [WA]iRonan
View Post
Then I read over it. Nice job though!
Quote:
Originally Posted by Alex Magaсa
View Post
Nice tutorial!
Quote:
Originally Posted by RedFusion
View Post
It's all pretty self-explanatory. But still, nice tutorial.
Thanks.
Reply
#19

....
Reply
#20

this is Very cool it's nice to see some professional scripters are scripting for 3.0z already it gives a push to try the new versions ^_^ cool
Reply


Forum Jump:


Users browsing this thread: 3 Guest(s)