Developer
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
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, ornilIsPlayerInQuest(source):truewhen the player is on a missionIsQuestActive(questId):truewhen any player is running that missionGetObjectiveStates(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
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):truewhen the character has finished the mission at least onceGetCompletedQuests(source): list of the mission IDs the character has finishedGetQuestCooldown(source, questId): seconds left on the cooldown,0when there is noneClearQuestCooldown(source, questId): removes the cooldown and returnstrue. Without Personal Cooldown the mission has one shared cooldown, so this clears it for every playerGetAvailableQuests(source, npcId?): what the NPC offers this player, as{ quests, playerXp, playerJob, playerJobGrade, playerGang, playerGangGrade }.nilfor an unknown NPC. WithoutnpcIdit 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
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 = truestarts it for the player's squad,options.npcGiverIdsets the NPC the mission returns to.CanPlayerStartQueststill runsCompleteQuest(source): completes the active mission and gives the rewards. When the mission returns to the NPC, the turn-in items are taken firstFailQuest(source, reason?): fails the mission for the whole squad.reasonis shown to the playersCancelQuest(source): cancels the mission for the whole squad
Objective Control
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 objectiveAdvanceObjective(source): completes the first active objective and returns its IDSetObjectiveProgress(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.
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 valuesetXP(source, npcId, amount): sets the reputation, never below0addXP(source, npcId, amount): adds reputation. Amounts of0or less are ignoredremoveXP(source, npcId, amount): removes reputation, never below0
Client Exports
Quest Givers
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 NPCsRefreshQuestGivers(): asks the server for the mission counts after about one second, then firesquestGiversUpdated. Calls within that second are merged into one
Each entry in the list:
id,name: NPC ID and name from the creatorcoords: list ofvector3, one for each position where the NPC stands right nowvisible: false outside the NPC's active hours, unless the player has an active mission from that NPC.coordsis empty whenvisibleis falsetotalQuests: missions the NPC offers the player. Locked missions with Hide While Locked on are not countedavailableQuests:totalQuestsminus the One-Time missions the player has already finished. Prerequisites, Required XP, cooldowns, job, gang and item requirements are not checkedcompleted: 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 arenilblip: the NPC blip settings from the creator as{ enabled, sprite, color, scale, name }, ornilwhen none are set. An empty or missingnamemeans 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
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 datastatus:'inactive','active','paused'or'completed'optional:truefor an optional objectivecurrent,total: progress,niluntil the objective reports itblips: the blips of this objective, with the same fields asOnObjectiveBlip.coordsfollows the entity when the blip is attached to one
Client Events
1AddEventHandler('ml_missions:client:questGiversUpdated', function(givers) end)
2AddEventHandler('ml_missions:client:objectivesUpdated', function(objectives) end)questGiversUpdated: the same list asGetQuestGivers. 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 afterRefreshQuestGivers. ox_core does not report the unloadobjectivesUpdated: the same list asGetActiveObjectives. 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
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.playerslists everyone who started itobjectiveCompleted: an objective is completedquestCompleted: a mission is completed, after the rewards.memberslists the squad members still in the missionquestFailed: a mission fails, with the reason
Server Handlers
Located in open/handlers_server.lua.
Permissions
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)
9endHasCreatorPermission: access to/missioncreator, mission and NPC editing and the Live Missions tabHasLivePermission: access to force complete, force fail and teleport. WithoutLiveActionsit uses the Creator check
Mission Flow
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)
22endIsPlayerDead: change it if your death script keeps its own dead stateCanPlayerStartQuest: returnfalse, 'reason'to block a missionModifyRewards: edit the money and item lists before they are givenOnQuestComplete: runs after the rewards.rewardDataholdsrewards,rewardConfigandreputationPointsOnQuestFailed: a mission fails from a timeout, death or cancelOnObjectiveComplete: 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.
1Handlers.OnVehicleKeptForPlayer = function(source, payload)
2 return nil
3endpayload:{ netId, model, plate, colors, damage, questId }- Return
truewhen you saved the vehicle yourself.nilorfalseuses the built-in save andConfig.Settings.KeepOnComplete
Client Handlers
Located in 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)
29endCanInteractWithNPC: returnfalseto hide the target option of an NPCBeforeOpenUI: returnfalseto block a mission windowOnMissionStart,OnMissionComplete,OnMissionFailed: mission events on the clientOnObjectiveComplete: an objective is completed on the client. The first argument is the objective ID, despite its name in the fileOnObjectiveBlip: runs for each objective blip once it is set up. Changedata.sprite,data.color,data.scaleordata.nameto restyle it, or returnfalseto hide it and draw your own icon. On an area blip only color can be changedOnObjectiveBlipRemoved: the blip is gone. Remove your own icon fordata.idhereOnEnemyPedReady: 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 locationsKILL_PEDS: kill enemy NPCs, zombie mode includedDEFEND_AREA: hold a location against waves of enemiesESCORT: bring an NPC to a destinationTAKE_VEHICLE: take a vehicle and deliver itGIVE_VEHICLE: hand the player a vehicle for the mission, optionally kept afterwardsINTERACT_PROGRESS: interact with a prop through a progress bar, a minigame or a door unlockRETRIEVE_ITEM: bring back items from the player's inventoryCHECK_ITEM: check that the player owns an itemHEIST_LOOT: grab points from the heist presets, with synced animations, an optional minigame and rewards per pointGATHER_REWARD: collect reward points at set locationsINTERACTION_TALK: talk to an NPC, with optional voiceCAMERA: a cinematic of up to 32 shots, a single shot, or a security camera networkTURN_IN: return to the NPC. Added on its own when the mission needs it
Linked Objectives
activateOnStart: starts this objective when another one startsactivateOnEvent: starts this objective when another one completespauseUntilComplete: 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 ondispatch.chance: chance from0to100.50by defaultdispatch.jobs: jobs that receive it, comma separated.'police'by defaultdispatch.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 startrequiredJob: jobs or gangs allowed to start, comma separated. One match is enoughrequiredJobsOnline: minimum online players per job, such as{ { job = 'police', count = 2 } }requiredItemName,requiredItemCount: item needed to start.removeItemOnStarttakes itcooldown: seconds before the mission can be taken again.isPersonalCooldownmakes it per playerisOneTimeOnly: each player can finish it onceprivateInstance: 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 onisPersonalCooldown.ox_doorlockdoors stay shared between buckets, and spawn scripts can change the bucket, so start the mission after the character has spawnedreturnToNpc: the player must go back to the NPC to turn intimeLimit: seconds to finish,0for no limitreputationPoints: reputation given on completion.xpPenaltyOnFailtakes reputation on failuresquadOptions.enabled,squadOptions.minMembers,squadOptions.maxMembers: squad mode and the squad size limitsrequiredQuests: missions the player must have finished first. Locked missions show as locked or stay hiddenrequiredSkills,requiredCategories:ml_skillsskills or category levels needed to startunlockSkills: skills unlocked on completionxpAmount,xpCategory: skill XP on completion, capped byConfig.Skills.MaxXpPerQuestvoice: briefing audio, ElevenLabs or a custom fileshowAllObjectives: 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 positionped.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
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
8end1Handlers.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
9end1Handlers.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
7end1RegisterNetEvent('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)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)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