# Custom missions

{% hint style="info" %}
To create a custom mission, whether single-player or multiplayer, you first need to understand which files are required and how to edit/use them properly.
{% endhint %}

{% stepper %}
{% step %}

### Create a basic config template

Locate the config/missions.lua file and create a basic template (example bellow)

{% tabs %}
{% tab title="Singleplayer" %}

```lua
    {
        label = 'Custom Mission Example',
        description = 'Your Custom Mission Example description for gang-activities.',
        icon = 'assets/avatars/male_5.png',
        reward = 100,
        xpReward = 50,
        difficulty = 'easy',
        required_level = 1,
        mission = 'custom_mission_exemple',
        endMissionRespawn = false,
        respawnRandomPos = {
            vector3(-57.509113311768, -1079.9354248047, 26.964902877808),
            vector3(377.14935302734, -1306.2001953125, 33.484718322754),
            vector3(-823.34063720703, -1094.5144042969, 11.145439147949),

        },
        blip = { -- set blip = nil so you don't activate the blip menu
            id = 119,
            color = 4,
            scale = 0.6,
            coords = true,

            name = 'Custom Mission Blip'
        },
        tasks = {
            [1] = {
                info = 'Custom singleplayer mission task 1',
                completeFunction = function(tasksArgs)
                    local arg1 = tasksArgs[1]


                    return false;
                end,
            },
        }
    },
```

{% endtab %}

{% tab title="Multiplayer" %}

```lua
    ['custom-multiplayer-mission'] = {
        label = 'Custom multiplayer Mission',
        description = 'Custom multiplayer Mission description',
        icon = 'assets/avatars/default.png',
        mission_color = 'green',
        mission_background = 'store-robbery', -- .png file name from assets/missions-bg/
        infos = {
            'Info 1',
            'Info 2',
            'Info 3',
        },
        endMissionRespawn = false,
        respawnRandomPos = {
            vector3(-57.509113311768, -1079.9354248047, 26.964902877808),
            vector3(377.14935302734, -1306.2001953125, 33.484718322754),
            vector3(-823.34063720703, -1094.5144042969, 11.145439147949),

        },

        blip = { -- set blip = nil so you don't activate the blip menu
            id = 59,
            color = 1,
            scale = 0.6,
            coords = nil,
            forEntity = true,
            setRoute = true,
            name = 'Custom Mission'
        },

        difficulty = 'easy',
        required_level = 1,
        xpReward = 60,
        reward = 35000,
        multiplayer = true, -- don't change
        setVirtualWorld = false,
        tasks = {
            [1] = {
                info = 'Custom Task 1',
                completeFunction = function(tasksArgs)
                    local task1 = tasksArgs[1];
                    return task1
                end,
            },
        }
    },
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Note: In order to create a high quality mission script you need to add more data in this mission table, such as random mission locations/ rewards / tasks etc.
{% endhint %}
{% endstep %}

{% step %}

### Create the server-side file for your mission.

Navigate to server/custom/missions/singleplayer/ or server/custom/missions/multiplayer/ and use the default template located there.&#x20;

{% tabs %}
{% tab title="Singleplayer" %}

```lua
---@type string unique and same as client side
local missionId <const> = 'custom_mission_exemple';

---comment
---@param playerSource integer
---@param missionData table
---@return table?nil, table?nil
local missionFunction = function(playerSource, missionData)
    if not missionData then return print('ERROR: MissionData is nil') end;

    local extraData = {};
    local entities = {};

    -- custom singleplayer missions for server side;
    -- insert all the entities in entities table with table.insert(entities, {entityData}), so after the mission is finished/failed or resource is restarted the entities will be deleted
    -- extraData need to contain all the entities informations created in server side such as: networkId, weaponName for npc enemy, extra mission data such as selected location/house etc
    -- even if you are not using extraData and entities make sure to let it an empty table, = {}
    -- make sure to set entities in virtual world if the missionData.setVirtualWorld = true;
    -- example:

    local npc; -- npc entity after you create it;

    local playerIdentifier = Framework.GetPlayerIdentifier(playerSource);

    if missionData.setVirtualWorld then
        local bucketId = 100 + playerSource;
        SetEntityRoutingBucket(npc, bucketId);
    end

    return extraData, entities;
end

-- be sure to add this code line if you want to crete the mission;

-- exports['axr_gang-activities']:insertSingleMission(missionId, missionFunction);

```

{% endtab %}

{% tab title="Multiplayer" %}

```lua
---@type string unique and same as client side
local missionId <const> = 'custom_multiplayer_mission_exemple';

---comment
---@param playerSource integer -- leader indentifier
---@param missionData table
---@param lobbyData table
---@return table?nil, table?nil
local missionFunction = function(playerSource, missionData, lobbyData)
    if not missionData then return print('ERROR: MissionData is nil') end;

    local extraData = {};
    local entities = {};

    -- custom multiplayer missions for server side;
    -- insert all the entities in entities table with table.insert(entities, {entityData}), so after the mission is finished/failed or resource is restarted the entities will be deleted
    -- extraData need to contain all the entities informations created in server side such as: networkId, weaponName for npc enemy, extra mission data such as selected location/house etc
    -- even if you are not using extraData and entities make sure to let it an empty table, = {}
    -- make sure to set entities in virtual world if the missionData.setVirtualWorld = true;
    -- example:

    local npcEntity;
    -- set entity in multiplayer mission virtual world;
    -- lobbyData.bucket is generated on lobby created;

    if missionData.setVirtualWorld then
        local bucketId = lobbyData.bucket;
        SetEntityRoutingBucket(npcEntity, bucketId);
    else
        SetEntityRoutingBucket(npcEntity, 0);
    end

    -- add this code lines to have all lobby memebers sources in client side;
    extraData.missionPlayers = {};

    for k, v in pairs(lobbyData.players) do
        local playerSource = Framework.GetSourceFromIdentifier(v.identifier);
        table.insert(extraData.missionPlayers, playerSource);
    end
    -- extraData.missionEvents is like the mission progress, for example extraData.missionEvents['event_1'] can be the task_1, task_2 or any condition to make progress in client side
    extraData.missionEvents = {
        ['event_1'] = false,
    }


    -- extraInformations:
    -- you can add custom RegisterServerEvents so you can sync missionEvents server side - client side for all players: example bellow
    return extraData, entities;
end

RegisterServerEvent('axr_gang-activities:trySyncCustomMissionEvent_1', function(params)
    local src = source;
    local identifier = Framework.GetPlayerIdentifier(src);
    
    if identifier then
        local lobbyIndex = exports['axr_gang-activities']:getPlayerLobby(identifier);
        if lobbyIndex and lobbies[lobbyIndex] then
            if not lobbies[lobbyIndex].missionEvents then
                lobbies[lobbyIndex].missionEvents = {};
            end
            
            if lobbies[lobbyIndex].missionEvents['event_1'] then
                return;
            end

            lobbies[lobbyIndex].missionEvents['event_1'] = true;
            TriggerClientEvent('axr_gang-activities:syncCustomMissionEvent_1', src); -- extra trigger event for client side

            -- code exemple to update missionEvents for every lobby memeber;
            for k, v in pairs(lobbies[lobbyIndex].players) do
                local playerSource = Framework.GetSourceFromIdentifier(v.identifier);
                TriggerClientEvent('axr_gang-activities:updateMissionEvents', playerSource, 'event_1', true);
            end
        end
    end
end)

-- be sure to add this code line if you want to crete the mission;

-- exports['axr_gang-activities']:insertMultiplayerMission(missionId, missionFunction);

```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Note: safe this file and rename it to a desire label. Also you need to create custom code lines in order to work, such as CreateVehicle, CreatePed, custom threads etc.
{% endhint %}
{% endstep %}

{% step %}

### Create the client-side file for your mission.

Navigate to client/custom/missions/singleplayer/ or client/custom/missions/multiplayer/ and use the default template located there.&#x20;

{% hint style="info" %}
Note: safe this file and rename it to a desire label. Also you need to create custom code lines in order to work, such as CreateVehicle, CreatePed, custom threads etc.
{% endhint %}

{% tabs %}
{% tab title="Singleplayer" %}

```lua
---@type string unique and same as server side
local missionId <const> = 'custom_mission_exemple';

---comment
---@param missionData table
---@param extraData table
local missionFunction = function(missionData, extraData)
    if not missionData then return end; 
    if not extraData then return end;

    -- custom singleplayer missions for client side;
    -- use missionData and extraData from server side to take control of entities/ set multiple blips and make your mission as you want

    inMissions = true -- need to set inMissions = true before creating mission overlay

    -- create blip with createMissionBlip function

    local blip;
    local blipSetter = vector3(1834.9813232422,-1922.0208740234,149.18962097168); -- blip setter need to be entity or vector3 coord, set forEntity = true or coords = true in config/missions.lua for your mission as you want;
    if missionData.blip then
        blip = createMissionBlip(missionData.blip, blipSetter)
    end


    -- create and show mission overlay code:

    local missionDataCopy = overlayFunctions.createMissionCopy(missionData);
    overlayFunctions.createMissionOverlay(missionDataCopy);
    overlayFunctions.createOverlaytoogleThread();



    Citizen.CreateThread(function()
        ---@type table
        local playerTempStats = {}
        local condition = true;
        local waiter = 1000;

        local failed = false;
        local failed_condition = false;
        local taskParameter1, taskParameter2;
        local condition_made_kill = false;
        while condition do
            Citizen.Wait(waiter);

            -- add verification for mission tasks,
            ---@taskParameter1 any value you want (the logic for completing the task is made in config/missions.lua)
            ---@taskParameter2 any value you want (the logic for completing the task is made in config/missions.lua)
            ---@params count = mission tasks count

            overlayFunctions.checkCompletedTasks(missionData, { taskParameter1, taskParameter2, });

            if condition_made_kill then -- example to update player kills statistics. For more information check our documentation
                playerTempStats['missions_kills'] = {
                    addValue = true,
                    value = 1
                }
            end

            ---@type boolean -- set failed_condition for your custom mission
            if failed_condition then
                failed = true;
            end

            -- break the mission and notify the player for failing it
            if failed then
                -- Notify(fail_message, notify_type)
                break;
            end
        end

        -- delete blips after mission failed/completed

        if blip and missionData.blip then
            RemoveBlip(blip)
        end

        inMissions = false -- be sure to set inMissions = false after mission failed/completed

        -- respawn random player after failed/completed, coords can be added/edited in config/missions.lua

        if missionData.endMissionRespawn then
            respawnPlayerFromMission(missionData.respawnRandomPos, missionData.success)
        end

        -- destroy mission overlay;
        overlayFunctions.destroyMissionOverlay();

        -- be sure to add this TriggerServerEvent to update player statistics and give reward if missionData.success = true;

        TriggerServerEvent('axr_gang-activities:finishMission', missionData, playerTempStats)
    end)
end

-- be sure to add this code line if you want to crete the mission;

-- exports['axr_gang-activities']:insertSingleMission(missionId, missionFunction);

```

{% endtab %}

{% tab title="Multiplayer" %}

```lua
---@type string unique and same as server side
local missionId <const> = 'custom_multiplayer_mission_exemple';

---comment
---@param missionData table
---@param extraData table
---@param isLeader boolean
local missionFunction = function(missionData, extraData, isLeader)
    if not missionData then return end;
    if not extraData then return end;

    -- custom singleplayer missions for client side;
    -- use missionData and extraData from server side to take control of entities/ set multiple blips and make your mission as you want

    -- use isLeader so you can take control of entities and edit them, example bellow

    local netId;
    local npcEntity;

    if not netId then
        return print("netId doesn't exist")
    end
    local timeout = GetGameTimer() + 5000

    repeat
        npcEntity = NetToPed(netId)
        Wait(1)
    until DoesEntityExist(npcEntity) or GetGameTimer() > timeout

    if isLeader then
        if not DoesEntityExist(npcEntity) then
            print("❌ Error not exist in client-side")
            return
        end

        SetPedCanRagdoll(npcEntity, true)

        NetworkRequestControlOfEntity(npcEntity)
        while not NetworkHasControlOfEntity(npcEntity) and GetGameTimer() < timeout do
            Wait(10)
        end

        if not NetworkHasControlOfEntity(npcEntity) then
            print("❌ Error getting entity control")
            return
        end
    end

    inMissions = true -- need to set inMissions = true before creating mission overlay

    -- create blip with createMissionBlip function

    local blip;
    local blipSetter = vector3(1834.9813232422, -1922.0208740234, 149.18962097168); -- blip setter need to be entity or vector3 coord, set forEntity = true or coords = true in config/missions.lua for your mission as you want;
    if missionData.blip then
        blip = createMissionBlip(missionData.blip, blipSetter)
    end


    -- create and show mission overlay code:

    local missionDataCopy = overlayFunctions.createMissionCopy(missionData);
    overlayFunctions.createMissionOverlay(missionDataCopy);
    overlayFunctions.createOverlaytoogleThread();



    Citizen.CreateThread(function()
        ---@type table
        local playerTempStats = {}
        local condition = true;
        local waiter = 1000;

        local failed = false;
        local failed_condition = false;
        local taskParameter1, taskParameter2;
        local condition_made_kill = false;
        while condition do
            Citizen.Wait(waiter);

            -- add verification for mission tasks,
            ---@taskParameter1 any value you want (the logic for completing the task is made in config/missions.lua)
            ---@taskParameter2 any value you want (the logic for completing the task is made in config/missions.lua)
            ---@params count = mission tasks count

            overlayFunctions.checkCompletedTasks(missionData, { taskParameter1, taskParameter2, });

            if condition_made_kill then -- example to update player kills statistics. For more information check our documentation
                playerTempStats['missions_kills'] = {
                    addValue = true,
                    value = 1
                }
            end

            ---@type boolean -- set failed_condition for your custom mission
            if failed_condition then
                failed = true;
            end

            -- break the mission and notify the player for failing it
            if failed then
                -- Notify(fail_message, notify_type)
                break;
            end
        end

        -- delete blips after mission failed/completed

        if blip and missionData.blip then
            RemoveBlip(blip)
        end

        inMissions = false -- be sure to set inMissions = false after mission failed/completed

        -- respawn random player after failed/completed, coords can be added/edited in config/missions.lua

        if missionData.endMissionRespawn then
            respawnPlayerFromMission(missionData.respawnRandomPos, missionData.success)
        end

        -- destroy mission overlay;
        overlayFunctions.destroyMissionOverlay();

        -- be sure to add this TriggerServerEvent to update player statistics and give reward if missionData.success = true;

        TriggerServerEvent('axr_gang-activities:finishMission', missionData, playerTempStats)
    end)
end

-- be sure to add this code line if you want to crete the mission;

-- exports['axr_gang-activities']:insertMultiplayerMission(missionId, missionFunction);

```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Sync the logic between these files.

Now that you have created all three files, you need to implement the logic that will make the code behave like a mission.

**Recommendation:**

* Store all mission data such as coordinates, rewards, ped/vehicle/prop codes inside the mission’s config table.
* Create entities on the **server-side**.
* Add entities to the global `entities` table using `table.insert(entities, entity)`.
* Return all necessary mission information inside the `extraData` table so it can be used later on the client-side.
* On the client-side, ensure that entity control is assigned to a single client.
* Use boolean parameters for tasks, or apply simple checks inside the task’s `completeFunction`.
* Follow the existing templates to avoid issues with blip creation, mission overlay handling (start/stop), etc.
* Use logical expressions to detect whether the mission has failed or not.
* At the end of the mission, make sure to remove temporary blips, destroy the mission overlay, and clean up client-side entities. (On the server-side, entities are automatically removed.)

{% hint style="danger" %}
There is no need to manually set **XP, level, rewards, or statistics** at the end of a mission.\
Instead, use the **default templates** provided within the script or documentation and follow the corresponding instructions.
{% endhint %}
{% endstep %}
{% endstepper %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.redutzu.com/resources/gang-activities/guides/custom-missions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
