[Tutorial] How to create missions in SA-MP (Missions as in player missions, not SP)
#1

Hello
Let's begin.

I'll be happy if this helps ONE genuine guy! So please don't reply if you have not read it!\
Using:

1) Streamer
2) ZCMD
3) Basic knowledge of PAWN (or any language, basically!) and a bit of logic


Algorithm / How to approach our scripting?

1) Choose a "mission" (Eg - delivering a package from point A to point B)
2) What would be the steps required? -
3) Picking out co - ordinates for players to deliver packages, many of them, so that we can randomly create destinations
4) Assign a spot where a player can start the mission using a command
5) Give the player the "package" (IE a variable)
6) Check if player's reached the destination and give him a reward if he has.

Easy? Let's go.

1) Step 1 - randomising the destinations / creating an array for that

You'll have to go ingame, and use /save to record CO - ORDINATES for destinations. I am using randomly typed values, which may be out of the scope of GTA: SA. So, you would create an array for this - later you can just randomly pick out a set of co-ordinates to create a destination at - so that the mission doesn't get boring/repetetive.
Let's name the array "random_destinations". Since it's an array containing float values, you'll have to tag it with the prefix "Float:". For more information, read up weak/strong tags on the WIKI. If you don't know about arrays, go for the wiki.

pawn Code:
new Float: random_destinations[][3] =
{
    1241.2, 1241.1, 12.6,
    51, 126, 12.5
};
[] because we do not know HOW many set of co-ords will we be adding. [3] because there are 3 things we need to save - the X co - ord, the Y co - ord and the Z co - ord, plus the \0 null terminator (0, 1, 2, 3 indices save X, Y, Z and \0 respectively.)

2) Step 2 - choosing a spot for a player to start mission

pawn Code:
#define MISSION_START_X 124.5
#define MISSION_START_Y 54.1
#define MISSION_START_Z 121.1
#define MISSION_START_CP_SIZE 2
Defines (declared using #define <define_name> <value>) are constant values you can assign to - i.e. where ever you use them, they will be replaced by their counterparts in the code. Please read about it on the wiki. They allow you to be non specific in tutorials like these.
Here, you can edit these values to where you want the player to be able to type a command and start the mission, and also the size of the checkpoint.
Now, you create the checkpoint there. The streamer function for creating a checkpoint is CreateDynamicCP (If you don't know what it is - please ****** it and research about it. Basically it helps you overcome SA-MP's ability to create just ONE checkpoint and instead create hundreds or thousands.)

So now, creating a checkpoint to allow a player to start the mission
pawn Code:
new job_cp;

public OnGameModeInit()
{
     job_cp = CreateDynamicCP(MISSION_START_X, MISSION_START_Y, MISSION_START_Z, MISSION_START_CP_SIZE);
     return 1;
}
Okay, so we create the checkpoint when the gamemode loads. We store the value of the checkpoint in a global variable "job_cp" to use it later.


3) Step 3 - creating a command for the player to start mission


Here, we need to make sure the player is in the checkpoint and he's typing the correct command for it.
Also, we need to create a variable to see if a player is in a mission or not. I prefer to use a boolean variable (which takes in true and false values) because that's what we are going to need. Is Player doing a mission or not?
Let's name the variable "isPlayerInMission"
pawn Code:
new bool: isPlayerInMission[MAX_PLAYERS] = false; // array, because we want to store the value for EACH player

Also, We will be using the function "IsPlayerInDynamicCP(playerid, cpid)" to check if he's in "job_cp" checkpoint.

pawn Code:
CMD:startmission(playerid, params[]) // No explanations - that's the way you declare a ZCMD command
{
    if( !IsPlayerInDynamicCP( playerid, job_cp ) ) return SendClientMessage(playerid, -1, "You are not in the checkpoint!"); // Okay, the dude isn't in the checkpoint? Send an error message else allow him to do the mission
    else
    {
           if( !isPlayerInMission[ playerid ] ) // Same as doing if( isPlayerInMission[ playerid] == false )
           {
                 
               // he isn't doing a mission
               isPlayerInMission[ playerid ] = true; // he is now doing one
               /* To make this more dynamic and non specific, we use a stock to set the player up for a job instead of typing the code here */
               Player_SetUpForMission( playerid ); // stock coming in later, ignore
           }
           else // he's already doing a mission!
                return SendClientMessage(playerid, -1, "You are already doing a mission.");
    }
   return 1;
} // complete all braces carefully - count opening and closing and see if they match
Explanations given in.

Stock:
pawn Code:
stock Player_SetUpForMission( playerid )
{
   // What ever you wanna do as a mission
    SendClientMessage(playerid, -1, "You have started a mission."); // Bless him, notify him
   // Incomplete
Now we want a destination for our player to reach and complete his mission right? Let's create a ARRAY to hold his checkpoint ID because we want to make it only visible to the player. Right?
pawn Code:
new playerCP_array[ MAX_PLAYERS ]; // now can hold the value of the CP ID

stock Player_SetUpForMission( playerid )
{
   // What ever you wanna do as a mission
    SendClientMessage(playerid, -1, "You have started a mission."); // Bless him, notify him
    new rand = random(sizeof( random_destinations ) ); // chooses a random set of co - ords out the array
    playerCP_array[ playerid] = CreateDynamicCP( random_destinations[rand][ 0 ] , random_destinations[rand][ 1 ], random_destinations[rand][ 2 ], 2 /* size */, 0 /* world id */, 0 /* interior id */, playerid ); // Okay now notice how we did this.
    /* We got rand - a random number out the size of the array. It's size currently is 2. (0, 1 indices). Random function gets random out of 0, 1. Okay, say we get 0. Now random_destinations[rand][ 0 ] looks like random_destinations[0][ 0 ]...
    Note the playerid parameter in the CreateDynamicCP function. This means the CP is ONLY VISIBLE TO THE PLAYER.
    We assign it's ID to the array we made. Now it's stored and we use it nicely.
   */


    SendClientMessage(playerid, -1, "Your destination has been created, shoot!");
    return 1;
}
Now what this stock does is just create a destination and instruct the player to get there. You can add stuff like attaching an object (say, a bag to drop off there) or a pickup or whatever stuff. You can make the mission VEHICLE SPECIFIC by checking if the player is in the vehicle MODEL ( say infernus - GetVehicleModel( GetPlayerVehicleID(playerid) ) == 411 ) ), etc.. Enjoy playing around.

4) Step 4 - has the player reached his destination?

We use the callback
pawn Code:
forward OnPlayerEnterDynamicCP(playerid, checkpointid);
to check this.
What will we check?
1) Is player doing a mission?
2) Is player in the CP?

pawn Code:
public OnPlayerEnterDynamicCP(playerid, checkpointid)
{
   if( isPlayerInMission[ playerid ] ) // if yes, is he in the CP?
   {    
    if( checkpointid == playerCP_array[ playerid ] ) // Is he in his OWN CP?
    {
               // okay he is in his cp, he was doing his mission, reward him for his hard work
               
               isPlayerInMission[playerid] = false; // he isn't doing it now!
               Reward_PlayerOnMissionComplete( playerid );
        }
   }
 return 1;
}
Now customise the stock Reward_PlayerOnMissionComplete( playerid ); to whatever you like! Give him cash, vehicles, etc..

pawn Code:
stock Reward_PlayerOnMissionComplete( playerid )
{
      SendClientMessage( playerid, -1, "You completed the mission!" );
      GivePlayerMoney(playerid, 1000);
      return 1;
}
That's it. Enjoy.

5) OPTIONAL Step 5 - giving him a reward based on the distance he travelled

Now since the mission can be started ONLY from one position in this tutorial, i'm directly doing this. If you have more than one checkpoint, use PVars, store the player's position when he starts the mission. If there's only one checkpoint -
pawn Code:
#define REWARD_CONST 350
stock Reward_PlayerOnMissionComplete( playerid )
{
      SendClientMessage( playerid, -1, "You completed the mission!" );
      new Float: dist = GetPlayerDistanceFromPoint(playerid, MISSION_START_X, MISSION_START_Y, MISSION_START_Z); // Make sure these are the co - ords he started his mission from!
      GivePlayerMoney( playerid, dist * REWARD_CONST );
      return 1;
}
Now this will give maximum money when the distance is the most. Play around with reward constant to get a suitable, not too high value (depends.)

Thank you.

TIPS:
1) Use simply named variables - keep it "playerCPposition" than something ridiculous "high level" programmers do which IMHO I find irritating, naming variables weirdly and complex- ly making it hard to remember their purpose (like "@sa_player_CP")
Personal opinion - free advice, take it or don't talk.

2) Think before you code - chalk out an algorithm.

3) You will need basic common sense.
Reply
#2

Very nice tutorial +rep for you
Reply
#3

Thanks a lot - glad if it helped you! I was thinking about MySQL saving of mission stats, but it would be rather pointless, seeing it's off topic (off the tutorial)
Reply
#4

+ BROO! awesome tutorial
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)