# Developer > Developer API reference for ML Missions Category: QUESTS SYSTEM · Source: https://miciomods.it/docs/ml-missions-developer · Last updated: 2026-07-28 ## Overview Integration uses server exports for quest state and XP, handler hooks in `open/handlers_server.lua` and `open/handlers_client.lua` for quest flow control, and a server event for quest completion. ## Server Exports ### Quest State ```lua local quest = exports.ml_missions:GetActiveQuest(source) local inQuest = exports.ml_missions:IsPlayerInQuest(source) ``` - `GetActiveQuest(source)`: Returns the active quest data table, or `nil` - `IsPlayerInQuest(source)`: Returns `true` if the player has an active quest ### XP / Reputation Per-NPC reputation tracked in the `ml_missions_xp` table. ```lua local xp = exports.ml_missions:getXP(source, npcId) exports.ml_missions:setXP(source, npcId, 100) exports.ml_missions:addXP(source, npcId, 25) exports.ml_missions:removeXP(source, npcId, 10) ``` - `getXP(source, npcId)`: Returns current XP value - `setXP(source, npcId, amount)`: Sets XP to a specific amount (min 0) - `addXP(source, npcId, amount)`: Adds XP - `removeXP(source, npcId, amount)`: Removes XP (cannot go below 0) ## Server Events ```lua AddEventHandler('ml_missions:server:questCompleted', function(source, questId, squadMembers) -- squadMembers is a table of source IDs (nil if solo) end) ``` Fires when any quest is completed, after rewards are distributed. ## Server Handlers Located in `open/handlers_server.lua`. ### Quest Flow **open/handlers_server.lua** ```lua function Handlers.IsPlayerDead(source) local ped = GetPlayerPed(source) if not ped or ped == 0 then return false end return GetEntityHealth(ped) <= 0 end function Handlers.CanPlayerStartQuest(source, questId, questConfig) return true, nil -- return false, 'reason' to block end function Handlers.ModifyRewards(source, questId, moneyData, itemsData) return moneyData, itemsData end ``` - `IsPlayerDead`: Override to integrate custom death/ambulance scripts - `CanPlayerStartQuest`: Return `false` + reason string to block quest start - `ModifyRewards`: Intercept and modify money/item arrays before distribution ### Quest Lifecycle **open/handlers_server.lua** ```lua function Handlers.OnQuestComplete(source, questId, questConfig, rewardData) end function Handlers.OnQuestFailed(source, questId, reason) end function Handlers.OnObjectiveComplete(source, questId, objectiveId, objectiveConfig) end ``` - `OnQuestComplete`: Fires after rewards are distributed. `rewardData` contains `rewards`, `rewardConfig`, and `reputationPoints`. - `OnQuestFailed`: Fires when a quest fails (timeout, death, manual cancel) - `OnObjectiveComplete`: Fires when a single objective is completed ### Vehicle Garage Hook When a `GIVE_VEHICLE` objective has `keepOnComplete = true`, ml_missions registers the vehicle natively (qbx_vehicles / player_vehicles / owned_vehicles). Override this hook to route it to a custom garage. **open/handlers_server.lua** ```lua function Handlers.OnVehicleKeptForPlayer(source, payload) -- payload = { netId, model, plate, colors, damage, questId } return nil -- nil/false = run native handler ; true = you handled the save end ``` - `OnVehicleKeptForPlayer`: Return `true` to take over the save and skip the native handler. Destination garage is set via `Config.Settings.KeepOnComplete`. ### Permission Hooks Both hooks resolve through `Bridge.HasPermission` against the tiers in `Config.Permissions`. **open/handlers_server.lua** ```lua Handlers.HasCreatorPermission = function(source) ... end Handlers.HasLivePermission = function(source) ... end ``` - `HasCreatorPermission(source)`: Gates `/missioncreator`, mission/NPC editing and the Live Missions tab. - `HasLivePermission(source)`: Gates force-complete / force-fail / teleport in the Live Missions tab. Falls back to `HasCreatorPermission` when `Config.Permissions.LiveActions` is nil. ## Client Handlers Located in `open/handlers_client.lua`. ### Interaction **open/handlers_client.lua** ```lua function Handlers.CanInteractWithNPC(npcId, npcData) return true -- return false to hide target end function Handlers.BeforeOpenUI(uiName) return true -- return false to block UI end ``` - `CanInteractWithNPC`: Gate NPC target visibility. Return `false` to hide. - `BeforeOpenUI`: Block UI opening. Return `false` to cancel. ### Mission Lifecycle **open/handlers_client.lua** ```lua function Handlers.OnMissionStart(questId, questConfig) end function Handlers.OnMissionComplete(questId, questConfig) end function Handlers.OnMissionFailed(questId, reason) end function Handlers.OnObjectiveComplete(objectiveIndex, objectiveConfig) end ``` - `OnMissionStart`: Fires when a quest starts on the client - `OnMissionComplete`: Fires when a quest is completed - `OnMissionFailed`: Fires when a quest fails - `OnObjectiveComplete`: Fires when an objective is completed ## Objective Types The mission creator supports these objective types: - `GOTO_COORDS`: Navigate to one or more locations - `KILL_PEDS`: Eliminate enemy NPCs (supports zombie mode) - `DEFEND_AREA`: Defend a location against waves of enemies - `ESCORT`: Escort an NPC to a destination - `TAKE_VEHICLE`: Pick up or deliver vehicles - `INTERACT_PROGRESS`: Interact with props using progress bars, minigames, or door unlocks - `RETRIEVE_ITEM`: Collect specific items from the player's inventory - `CHECK_ITEM`: Verify the player owns a specific item - `HEIST_LOOT`: Scripted grab points driven by heist presets, with synced animations, optional minigame and per-point rewards - `GATHER_REWARD`: Collect reward points at specific locations - `INTERACTION_TALK`: NPC dialogue interaction with optional TTS - `TURN_IN`: Auto-generated return-to-NPC objective ### Objective Dependencies Objectives can be linked together: - `activateOnStart`: Activate this objective when another objective starts - `activateOnEvent`: Activate on a specific event (format: `"objectiveId:eventName"`) - `pauseUntilComplete`: Pause this objective until a dependency completes ### Dispatch Integration Each objective can trigger a police dispatch notification: - `dispatch.enabled`: Enable dispatch for this objective - `dispatch.chance`: Trigger probability (0-100) - `dispatch.jobs`: Comma-separated job list (default: `'police'`) - `dispatch.message`: Custom dispatch message ### Heist Presets Each `HEIST_LOOT` point references a preset from `shared/heist_presets.lua`. A preset defines the target prop spawned in the world, an optional prop swap once the loot is taken, and the phased grab animation with attached props, synced for every player nearby. - `cash_trolley`, `gold_trolley`: bank trolleys emptied into a duffel bag - `safe_crack`: floor safe with an open-door swap - `cash_stack`, `gold_stack`, `cocaine_stack`, `weed_stack`: table grabs - `jewel_case`: display case smash - `laptop_hack`: laptop grab - `bolt_cutters`, `container_grind`, `crate_crowbar`: tool entries for chains, containers and crates Add your own presets in the same file: copy an entry and change the props and animation phases. ### Waypoints and Markers Every objective location can enable an overhead 3D waypoint and a ground marker, together or separately, set per objective in the creator (fields `waypoint` and `marker3d`). ## Quest Options Configurable per-quest in the creator: - `requiredXp`: Minimum NPC reputation to start - `requiredJob`: Comma-separated list of jobs or gangs allowed to start the quest. Matching one entry is enough - `requiredJobsOnline`: Array of `{ job, count }` entries. Requires a minimum number of online players per job before the quest can start. Example: `{ { job = 'police', count = 2 }, { job = 'bcso', count = 1 } }` - `requiredItemName` / `requiredItemCount`: Item prerequisites - `removeItemOnStart`: Consume required item on quest start - `cooldown`: Seconds before the quest can be retaken - `isPersonalCooldown`: `true` = per-player cooldown, `false` = global cooldown - `isOneTimeOnly`: Quest can only be completed once per player - `returnToNpc`: Player must return to NPC for turn-in - `timeLimit`: Seconds to complete (0 = no limit) - `reputationPoints`: XP awarded on completion - `xpPenaltyOnFail`: XP deducted on failure - `squadOptions.enabled`: Enable squad mode - `squadOptions.minMembers`: Minimum squad members to start - `requiredSkills`: Array of ml_skills entries the player must have unlocked before starting - `requiredCategories`: Array of `{ category, minLevel }` checks against ml_skills category levels - `unlockSkills`: Skills granted to the player when the quest completes - `xpAmount` / `xpCategory`: Skill XP awarded on completion and the category it lands in, capped by `Config.Skills.MaxXpPerQuest` - `voice`: Briefing audio: `{ enableTTS, audioType = 'elevenlabs' | 'custom', voiceId, customAudioFile }` - `showAllObjectives`: Show the full objective list in the HUD instead of revealing steps one at a time ## NPC Configuration - `ped.coords`: Main spawn position (vector4) - `ped.positions`: Optional array of additional spawn positions for multi-location NPCs. Each entry: `{ coords = { x, y, z, w } }`. The same NPC spawns at all positions, shares quests and reputation, but the turn-in always routes back to the specific position where the player accepted the quest. ## Reward System Rewards are configured per-quest in the creator. Two formats: ### Fixed Loots Guaranteed drops from a defined pool of items. ### Probability Loots Percentage-based drops with configurable loop count. Each loop rolls against the drop chance independently. ### Money Random amount between a min and max threshold, to a specific account (`cash` or `bank`). ### Squad Bonuses Configurable per squad size. Applies a multiplier to money and item rewards when completing as a squad. ## Examples **Block quest during server events** **open/handlers_server.lua** ```lua local eventActive = false Handlers.CanPlayerStartQuest = function(source, questId, questConfig) if eventActive then return false, 'Missions are disabled during events' end return true end ``` **Double rewards for VIPs** **open/handlers_server.lua** ```lua Handlers.ModifyRewards = function(source, questId, moneyData, itemsData) local job = Bridge.GetJob(source) if job and job.name == 'vip' then for i, money in ipairs(moneyData) do moneyData[i].amount = money.amount * 2 end end return moneyData, itemsData end ``` **Hide NPC based on job** **open/handlers_client.lua** ```lua Handlers.CanInteractWithNPC = function(npcId, npcData) local job = Bridge.GetPlayerData()?.job?.name if npcId == 'police_chief' and job ~= 'police' then return false end return true end ``` **Grant skill XP on quest complete** **open/handlers_server.lua** ```lua Handlers.OnQuestComplete = function(source, questId, questConfig, rewardData) if questConfig.category == 'combat' then exports.ml_skills:AddXp('personal', 50, source) end end ```