Developer

13 min readUpdated Today

Overview

Other resources start, finish and drive missions through server exports, and listen to server events. Permissions, rewards and the mission flow are changed in open/handlers_server.lua and open/handlers_client.lua, which stay unencrypted.

Server Exports

Mission State

lua
1local quest = exports.ml_missions:GetActiveQuest(source)
2local inQuest = exports.ml_missions:IsPlayerInQuest(source)
3local running = exports.ml_missions:IsQuestActive('bank_job')
4local states = exports.ml_missions:GetObjectiveStates(source)
5local objectives = exports.ml_missions:GetQuestObjectives('bank_job')
  • GetActiveQuest(source): active mission data of the player, or nil
  • IsPlayerInQuest(source): true when the player is on a mission
  • IsQuestActive(questId): true when any player is running that mission
  • GetObjectiveStates(source): state of each objective of the active mission, by objective ID. 'inactive', 'active', 'paused' or 'completed'
  • GetQuestObjectives(questId): list of { id, type, description, isOptional }

Player Progress

lua
1local done = exports.ml_missions:HasCompletedQuest(source, 'bank_job')
2local finished = exports.ml_missions:GetCompletedQuests(source)
3local seconds = exports.ml_missions:GetQuestCooldown(source, 'bank_job')
4exports.ml_missions:ClearQuestCooldown(source, 'bank_job')
5local npcQuests = exports.ml_missions:GetAvailableQuests(source, 'old_tom')
6local allQuests = exports.ml_missions:GetAvailableQuests(source)
  • HasCompletedQuest(source, questId): true when the character has finished the mission at least once
  • GetCompletedQuests(source): list of the mission IDs the character has finished
  • GetQuestCooldown(source, questId): seconds left on the cooldown, 0 when there is none
  • ClearQuestCooldown(source, questId): removes the cooldown and returns true. Without Personal Cooldown the mission has one shared cooldown, so this clears it for every player
  • GetAvailableQuests(source, npcId?): what the NPC offers this player, as { quests, playerXp, playerJob, playerJobGrade, playerGang, playerGangGrade }. nil for an unknown NPC. Without npcId it returns one of these tables for each NPC, by NPC ID. It reads the database, so do not call it in a loop

Each entry in quests has id, title, requiredXp, requiredJob, personalCooldownRemaining, globalCooldownRemaining, isLockedGlobally, isOneTimeOnly, hasCompletedOneTime, hasRequiredItem, requiredItemName, requiredItemCount, requiredItemLabel and missingPrerequisite. Locked missions with Hide While Locked on are not listed.

Mission Control

lua
1local ok, err = exports.ml_missions:StartQuest(source, 'bank_job', { squad = true })
2exports.ml_missions:CompleteQuest(source)
3exports.ml_missions:FailQuest(source, 'The alarm went off')
4exports.ml_missions:CancelQuest(source)
  • StartQuest(source, questId, options?): starts a mission. options.squad = true starts it for the player's squad, options.npcGiverId sets the NPC the mission returns to. CanPlayerStartQuest still runs
  • CompleteQuest(source): completes the active mission and gives the rewards. When the mission returns to the NPC, the turn-in items are taken first
  • FailQuest(source, reason?): fails the mission for the whole squad. reason is shown to the players
  • CancelQuest(source): cancels the mission for the whole squad

Objective Control

lua
1exports.ml_missions:CompleteObjective(source, 'hack_terminal')
2local ok, objectiveId = exports.ml_missions:AdvanceObjective(source)
3exports.ml_missions:SetObjectiveProgress(source, 'collect_parts', 3, 5)
  • CompleteObjective(source, objectiveId): completes an active objective
  • AdvanceObjective(source): completes the first active objective and returns its ID
  • SetObjectiveProgress(source, objectiveId, current, total): shows progress such as 3 of 5 on an active objective

Every control export returns true, or false and an error code: 'invalid_player', 'not_ready', 'unknown_quest', 'already_in_quest', 'no_active_quest', 'unknown_objective', 'objective_not_active', 'objective_already_completed' or 'item_handover_failed'. When CanPlayerStartQuest blocks a start, StartQuest returns its reason. AdvanceObjective also returns the ID of the objective it completed.

Reputation

Each NPC keeps a reputation value per player in ml_missions_xp.

lua
1local xp = exports.ml_missions:getXP(source, npcId)
2exports.ml_missions:setXP(source, npcId, 100)
3exports.ml_missions:addXP(source, npcId, 25)
4exports.ml_missions:removeXP(source, npcId, 10)
  • getXP(source, npcId): current reputation. NPCs in the same reputation group share one value
  • setXP(source, npcId, amount): sets the reputation, never below 0
  • addXP(source, npcId, amount): adds reputation. Amounts of 0 or less are ignored
  • removeXP(source, npcId, amount): removes reputation, never below 0

Client Exports

Quest Givers

lua
1local givers = exports.ml_missions:GetQuestGivers()
2exports.ml_missions:RefreshQuestGivers()
  • GetQuestGivers(): list of the mission NPCs with their state for the local player. Empty until ml_missions has loaded the NPCs
  • RefreshQuestGivers(): asks the server for the mission counts after about one second, then fires questGiversUpdated. Calls within that second are merged into one

Each entry in the list:

  • id, name: NPC ID and name from the creator
  • coords: list of vector3, one for each position where the NPC stands right now
  • visible: false outside the NPC's active hours, unless the player has an active mission from that NPC. coords is empty when visible is false
  • totalQuests: missions the NPC offers the player. Locked missions with Hide While Locked on are not counted
  • availableQuests: totalQuests minus the One-Time missions the player has already finished. Prerequisites, Required XP, cooldowns, job, gang and item requirements are not checked
  • completed: true when the NPC has at least one counted mission and all of them are One-Time missions the player has finished. False while the counts are nil
  • blip: the NPC blip settings from the creator as { enabled, sprite, color, scale, name }, or nil when none are set. An empty or missing name means the NPC name is shown

Both counts are nil until the character is loaded and the server has answered, and again after the character unloads.

Active Objectives

lua
1local objectives = exports.ml_missions:GetActiveObjectives()
  • GetActiveObjectives(): every objective of the local player's active mission, empty when there is none

Each entry in the list:

  • questId, id, type, title, description: mission ID and objective data
  • status: 'inactive', 'active', 'paused' or 'completed'
  • optional: true for an optional objective
  • current, total: progress, nil until the objective reports it
  • blips: the blips of this objective, with the same fields as OnObjectiveBlip. coords follows the entity when the blip is attached to one

Client Events

lua
1AddEventHandler('ml_missions:client:questGiversUpdated', function(givers) end)
2AddEventHandler('ml_missions:client:objectivesUpdated', function(objectives) end)
  • questGiversUpdated: the same list as GetQuestGivers. Fires when the NPCs load, when the character loads or unloads, when an admin edits or removes an NPC, when an NPC opens, closes or moves, when a mission ends, and after RefreshQuestGivers. ox_core does not report the unload
  • objectivesUpdated: the same list as GetActiveObjectives. Fires when a mission starts or ends, when an objective changes state or progress, and when a blip is added or removed. Changes within 100 ms are merged into one call

To draw your own map icons, turn off the NPC blip in the creator and build the icons from this list.

Server Events

lua
1AddEventHandler('ml_missions:server:questStarted', function(source, questId, players) end)
2AddEventHandler('ml_missions:server:objectiveCompleted', function(source, questId, objectiveId, squad) end)
3AddEventHandler('ml_missions:server:questCompleted', function(source, questId, members) end)
4AddEventHandler('ml_missions:server:questFailed', function(source, questId, reason, members) end)

The first argument is the squad leader, or the solo player.

  • questStarted: a mission starts. players lists everyone who started it
  • objectiveCompleted: an objective is completed
  • questCompleted: a mission is completed, after the rewards. members lists the squad members still in the mission
  • questFailed: a mission fails, with the reason

Server Handlers

Located in open/handlers_server.lua.

Permissions

open/handlers_server.lua
1Handlers.HasCreatorPermission = function(source)
2    return checkPermissionTier(source, Config.Permissions and Config.Permissions.Creator)
3end
4
5Handlers.HasLivePermission = function(source)
6    local tier = Config.Permissions and Config.Permissions.LiveActions
7    if tier then return checkPermissionTier(source, tier) end
8    return Handlers.HasCreatorPermission(source)
9end
  • HasCreatorPermission: access to /missioncreator, mission and NPC editing and the Live Missions tab
  • HasLivePermission: access to force complete, force fail and teleport. Without LiveActions it uses the Creator check

Mission Flow

open/handlers_server.lua
1Handlers.IsPlayerDead = function(source)
2    local ped = GetPlayerPed(source)
3    if not ped or ped == 0 then return false end
4    return GetEntityHealth(ped) <= 0
5end
6
7Handlers.CanPlayerStartQuest = function(source, questId, questConfig)
8    return true, nil
9end
10
11Handlers.ModifyRewards = function(source, questId, moneyData, itemsData)
12    return moneyData, itemsData
13end
14
15Handlers.OnQuestComplete = function(source, questId, questConfig, rewardData)
16end
17
18Handlers.OnQuestFailed = function(source, questId, reason)
19end
20
21Handlers.OnObjectiveComplete = function(source, questId, objectiveId, objectiveConfig)
22end
  • IsPlayerDead: change it if your death script keeps its own dead state
  • CanPlayerStartQuest: return false, 'reason' to block a mission
  • ModifyRewards: edit the money and item lists before they are given
  • OnQuestComplete: runs after the rewards. rewardData holds rewards, rewardConfig and reputationPoints
  • OnQuestFailed: a mission fails from a timeout, death or cancel
  • OnObjectiveComplete: an objective is completed

Kept Vehicles

When a GIVE_VEHICLE objective lets the player keep the vehicle, the script saves it as an owned vehicle of your framework. Use this hook to send it to your own garage resource instead.

open/handlers_server.lua
1Handlers.OnVehicleKeptForPlayer = function(source, payload)
2    return nil
3end
  • payload: { netId, model, plate, colors, damage, questId }
  • Return true when you saved the vehicle yourself. nil or false uses the built-in save and Config.Settings.KeepOnComplete

Client Handlers

Located in open/handlers_client.lua.

open/handlers_client.lua
1Handlers.CanInteractWithNPC = function(npcId, npcData)
2    return true
3end
4
5Handlers.BeforeOpenUI = function(uiName)
6    return true
7end
8
9Handlers.OnMissionStart = function(questId, questConfig)
10end
11
12Handlers.OnMissionComplete = function(questId, questConfig)
13end
14
15Handlers.OnMissionFailed = function(questId, reason)
16end
17
18Handlers.OnObjectiveComplete = function(objectiveIndex, objectiveConfig)
19end
20
21Handlers.OnObjectiveBlip = function(data)
22    return true
23end
24
25Handlers.OnObjectiveBlipRemoved = function(data)
26end
27
28Handlers.OnEnemyPedReady = function(ped, weaponHash)
29end
  • CanInteractWithNPC: return false to hide the target option of an NPC
  • BeforeOpenUI: return false to block a mission window
  • OnMissionStart, OnMissionComplete, OnMissionFailed: mission events on the client
  • OnObjectiveComplete: an objective is completed on the client. The first argument is the objective ID, despite its name in the file
  • OnObjectiveBlip: runs for each objective blip once it is set up. Change data.sprite, data.color, data.scale or data.name to restyle it, or return false to hide it and draw your own icon. On an area blip only color can be changed
  • OnObjectiveBlipRemoved: the blip is gone. Remove your own icon for data.id here
  • OnEnemyPedReady: a mission enemy is set up and armed. Apply your own weapon damage rules to mission enemies here

data holds id, role, questId, objectiveId, objectiveType, coords, entity, radius, sprite, color and name. role is 'target', 'area', 'enemy', 'vip', 'escort', 'delivery', 'npc', 'vehicle', 'vehicle_spawn' or 'turn_in'. entity is set when the blip follows a ped or vehicle, radius when it is an area, and scale is nil unless you set it.

Hiding a blip also removes its route. ESCORT objectives still set the GPS waypoint, and these hooks do not change overhead waypoints or ground markers.

Objective Types

  • GOTO_COORDS: reach one or more locations
  • KILL_PEDS: kill enemy NPCs, zombie mode included
  • DEFEND_AREA: hold a location against waves of enemies
  • ESCORT: bring an NPC to a destination
  • TAKE_VEHICLE: take a vehicle and deliver it
  • GIVE_VEHICLE: hand the player a vehicle for the mission, optionally kept afterwards
  • INTERACT_PROGRESS: interact with a prop through a progress bar, a minigame or a door unlock
  • RETRIEVE_ITEM: bring back items from the player's inventory
  • CHECK_ITEM: check that the player owns an item
  • HEIST_LOOT: grab points from the heist presets, with synced animations, an optional minigame and rewards per point
  • GATHER_REWARD: collect reward points at set locations
  • INTERACTION_TALK: talk to an NPC, with optional voice
  • CAMERA: a cinematic of up to 32 shots, a single shot, or a security camera network
  • TURN_IN: return to the NPC. Added on its own when the mission needs it

Linked Objectives

  • activateOnStart: starts this objective when another one starts
  • activateOnEvent: starts this objective when another one completes
  • pauseUntilComplete: holds this objective until another one completes

Camera Objective

  • Cinematic: up to 32 shots with cut, linear or smooth transitions, subtitles and voice. Each shot looks at a saved direction, a world position or the player. Set up each shot in game with a free camera and preview it from the creator
  • Single shot: one camera shot
  • Security cameras: up to 16 cameras linked to a terminal. Cameras players must check, job and item access, and cameras that turn on or off after other objectives

In a squad, the cinematic starts for everyone once the members are together, within Config.Camera.SquadRadius.

Dispatch

Each objective can send a police alert:

  • dispatch.enabled: turns the alert on
  • dispatch.chance: chance from 0 to 100. 50 by default
  • dispatch.jobs: jobs that receive it, comma separated. 'police' by default
  • dispatch.message: alert text

Waypoints and Markers

Every objective location can show an overhead waypoint, a ground marker or both, set per objective in the creator.

Mission Options

Set per mission in the creator:

  • requiredXp: reputation with the NPC needed to start
  • requiredJob: jobs or gangs allowed to start, comma separated. One match is enough
  • requiredJobsOnline: minimum online players per job, such as { { job = 'police', count = 2 } }
  • requiredItemName, requiredItemCount: item needed to start. removeItemOnStart takes it
  • cooldown: seconds before the mission can be taken again. isPersonalCooldown makes it per player
  • isOneTimeOnly: each player can finish it once
  • privateInstance: Private Mission in the creator. Each player or squad plays in a separate routing bucket and goes back to the previous bucket when the mission ends. Saving turns on isPersonalCooldown. ox_doorlock doors stay shared between buckets, and spawn scripts can change the bucket, so start the mission after the character has spawned
  • returnToNpc: the player must go back to the NPC to turn in
  • timeLimit: seconds to finish, 0 for no limit
  • reputationPoints: reputation given on completion. xpPenaltyOnFail takes reputation on failure
  • squadOptions.enabled, squadOptions.minMembers, squadOptions.maxMembers: squad mode and the squad size limits
  • requiredQuests: missions the player must have finished first. Locked missions show as locked or stay hidden
  • requiredSkills, requiredCategories: ml_skills skills or category levels needed to start
  • unlockSkills: skills unlocked on completion
  • xpAmount, xpCategory: skill XP on completion, capped by Config.Skills.MaxXpPerQuest
  • voice: briefing audio, ElevenLabs or a custom file
  • showAllObjectives: shows the full objective list instead of one step at a time

Mission NPCs and vehicles always spawn in the routing bucket of the player who triggers them, with or without this option.

NPCs

  • ped.coords: main spawn position
  • ped.positions: extra positions. The same NPC spawns at each one with the same missions and reputation, and the turn-in goes back to the position where the player took the mission

Rewards

Set per mission in the creator:

  • Fixed loots: guaranteed items from a pool
  • Probability loots: chance drops, rolled a set number of times
  • Money: a random amount between a minimum and a maximum, to cash or bank
  • Clothing: with ML Inventory, a fixed piece or a random piece from a series
  • Squad bonus: a multiplier on money and items by squad size

Rewards that do not fit in the inventory are kept and given to the player later.

Examples

open/handlers_server.lua
1local eventActive = false
2
3Handlers.CanPlayerStartQuest = function(source, questId, questConfig)
4    if eventActive then
5        return false, 'Missions are closed during the event'
6    end
7    return true
8end
open/handlers_server.lua
1Handlers.ModifyRewards = function(source, questId, moneyData, itemsData)
2    local job = Bridge.GetJob(source)
3    if job and job.name == 'vip' then
4        for i, money in ipairs(moneyData) do
5            moneyData[i].amount = money.amount * 2
6        end
7    end
8    return moneyData, itemsData
9end
open/handlers_client.lua
1Handlers.CanInteractWithNPC = function(npcId, npcData)
2    local job = Bridge.GetJob()
3    if npcId == 'police_chief' and (not job or job.name ~= 'police') then
4        return false
5    end
6    return true
7end
your_script/server.lua
1RegisterNetEvent('myscript:terminalHacked', function()
2    local src = source
3    local ok, err = exports.ml_missions:CompleteObjective(src, 'hack_terminal')
4    if not ok then print(err) end
5end)
your_script/client.lua
1local blips = {}
2
3local function draw(givers)
4    for _, blip in ipairs(blips) do RemoveBlip(blip) end
5    blips = {}
6    for _, giver in ipairs(givers) do
7        if not giver.completed then
8            for _, coords in ipairs(giver.coords) do
9                local blip = AddBlipForCoord(coords.x, coords.y, coords.z)
10                SetBlipSprite(blip, 280)
11                SetBlipAsShortRange(blip, true)
12                blips[#blips + 1] = blip
13            end
14        end
15    end
16end
17
18AddEventHandler('ml_missions:client:questGiversUpdated', draw)
19
20CreateThread(function()
21    if GetResourceState('ml_missions') == 'started' then
22        draw(exports.ml_missions:GetQuestGivers())
23    end
24end)
open/handlers_client.lua
1local icons = {}
2
3Handlers.OnObjectiveBlip = function(data)
4    if data.role ~= 'target' then return true end
5    local blip = AddBlipForCoord(data.coords.x, data.coords.y, data.coords.z)
6    SetBlipSprite(blip, 280)
7    icons[data.id] = blip
8    return false
9end
10
11Handlers.OnObjectiveBlipRemoved = function(data)
12    if icons[data.id] then
13        RemoveBlip(icons[data.id])
14        icons[data.id] = nil
15    end
16end
Quests System Developer, FiveM Docs | Micio Mods