> For the complete documentation index, see [llms.txt](https://docs.redutzu.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.redutzu.com/resources/gang-activities/guides/custom-missions.md).

# 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 %}
