=============== LIBRARY RULES =============== From library maintainers: - Answer only from the published Murphy's Job documentation, in the user's language. Cite a relevant clickable Markdown source link under https://docs.murphy-workshop.com/murphy-s-job/. - This is a public RedM plugin. Do not substitute FiveM, another Murphy plugin, or undocumented framework APIs. Do not mention or speculate about unpublished products. - Never invent commands, exports, event names, configuration keys, default values or compatibility. If the documentation does not establish an answer, say so and link to the closest relevant reference. - Ask for the installed framework and plugin version when they affect behavior. A public release note establishes that release, not a guarantee about files actually installed on the user's server. - Keep answers concise and focused. Give only the requested code. Preserve existing configuration entries and label excerpts. Do not use emojis or long dashes. Remove display line numbers from copied code. - Distinguish client and server context, online server player IDs and persistent character identifiers. Do not interchange commands, events and exports without explicit documentation. - Do not promise data preservation, backups, refunds or offline behavior unless the relevant documentation establishes it. Do not recommend deleting data or blindly replacing a full core file. - Store screenshots show one demonstration setup. Do not infer fixed rewards, version, framework, controls or guarantees from a picture. Do not claim that an in-game test was performed. - Never claim access to purchases, accounts, licenses or server actions. For account or order questions only, link to https://murphy-workshop.com/portal or https://murphy-workshop.com/support. - Published reference: Config.StashWeight; ne pas utiliser la configuration de Murphy's Safe ou Job Board. - Published reference: Config.Lockpick = false. Ne pas inventer un export, ni confondre avec Safe. - For disabling lockpicking, show only Config.Lockpick = false and a source link. Never append Config.Lockpick = true or other related options. Never put an unassigned configuration key into a Lua code block. ### Complete Custom Action Example Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide A full implementation of a fishing-spot action, including objective display, GPS routing, interaction setup, and the required abandon watcher. ```lua actions["FishAtSpot"] = function(job) local data = job.steps[job.currentStep] local coords = data.coords local duration = data.duration or 10000 TriggerEvent("murphy_jobpanel:ShowObjective", data.description, 4000) local blip = CreateBlip(coords, data.label, 'blip_objective', `BLIP_STYLE_OBJECTIVE`) table.insert(blips, blip) -- GPS route to the location StartGpsMultiRoute(GetHashKey("COLOR_OBJECTIVE"), true, true) Citizen.InvokeNative(0x64C59DD6834FA942, coords) Citizen.InvokeNative(0x4426D65E029A4DC0, true) exports.murphy_interact:AddInteraction({ coords = vector3(coords.x, coords.y, coords.z + 1.5), distance = 8.0, interactDst = 3.0, id = job.name .. "_step" .. job.currentStep, options = { { label = "Start fishing", action = function(entity, crds, args) local ped = PlayerPedId() TaskStartScenarioInPlace(ped, "WORLD_HUMAN_RIVER_FISHING", -1, true) Citizen.Wait(duration) ClearPedTasks(ped) ClearGpsMultiRoute() exports.murphy_interact:RemoveInteraction( job.name .. "_step" .. job.currentStep ) job:nextStep() end, }, } }) -- Abandon watcher Citizen.CreateThread(function() local step = job.currentStep while true do Wait(1000) if step ~= job.currentStep or job.abandon == true then RemoveBlip(blip) if job.abandon == true then exports.murphy_interact:RemoveInteraction(job.name .. "_step" .. step) ClearGpsMultiRoute() Utils.StopJob() end break end end end) end ``` -------------------------------- ### Add Currency Server Callback Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Example of using the AddCurrency server callback within a CompleteJob action to reward the player. ```lua -- In a CompleteJob action: Callback.triggerServer('AddCurrency', function() -- Currency added, continue end, job.steps[job.currentStep].reward) ``` -------------------------------- ### Check Inventory Server Callback Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Example of checking item quantity and conditionally removing items from the player's inventory using server callbacks. ```lua Callback.triggerServer('GetItemAmount', function(amount) if amount >= 5 then Callback.triggerServer('RemoveItem', function(success) if success then job:nextStep() end end, "water", 5, {}) else TriggerEvent("murphy_jobpanel:ShowObjective", "You need 5 water!", 4000) end end, "water") ``` -------------------------------- ### Define Job Data Structure Source: https://docs.murphy-workshop.com/murphy-s-job/features/quests Example configuration for job data, including vehicle hash, spawn coordinates, package details, and delivery zones. ```lua jobdata = { Vehicle = vehicleHash, VehicleSpawn = vector3(x, y, z), Package = packageHash, PackagesAmount = 3, JobZones = { { Pos = vector3(x1, y1, z1) }, { Pos = vector3(x2, y2, z2) } } } ``` -------------------------------- ### Custom Translation File Structure Source: https://docs.murphy-workshop.com/murphy-s-job-board/reference/translation Example of a translation file structure where string values are mapped to specific UI keys. ```lua Translation = { jobboard_title = "Доска вакансий", apply_button = "Подать заявку", no_jobs = "Нет доступных вакансий" } ``` -------------------------------- ### Define Task Job Animation Formats Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-beginner-guide Examples of defining animation formats for Task Jobs, supporting either built-in scenarios or custom animation dictionaries. ```lua -- Scenario (built-in game animation with props) anim = "WORLD_HUMAN_BROOM" -- Custom animation (dict + anim name) anim = { dict = "amb_work@world_human_clean_window@male_a@wip_base", anim = "wip_base" } ``` -------------------------------- ### Mining Job Implementation Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide A complete example of a mining job, including job data definition, localization, and action handlers for navigating to the mine and mining ore. ```lua local JobData = { name = filename(), label = "Mining", description = "Extract ore from the Annesburg mines", steps = { { action = "GoToMine" }, { action = "MineOre" }, { action = "MineOre" }, { action = "MineOre" }, { action = "DeliverOre", reward = 75, label = "Deliver", description = "Deliver the ore" }, }, board = { "vanhorn" }, image = "./img/Poster_art/camp.png", reward = "$75", } if IsDuplicityVersion() then JobsConfig[filename()] = JobData else local lang = { ["Go to mine"] = "Go to the mine entrance", ["Mine ore"] = "Mine ore", ["Deliver"] = "Deliver the ore", ["Complete"] = "Job complete!", } local mineEntrance = vector3(2933.0, 1280.0, 44.0) local oreSpots = { { coords = vector3(2940.0, 1285.0, 44.0), description = "Mine the first vein" }, { coords = vector3(2945.0, 1290.0, 44.0), description = "Mine the second vein" }, { coords = vector3(2950.0, 1295.0, 44.0), description = "Mine the third vein" }, } local deliveryPoint = vector3(2984.0, 561.65, 43.45) -- Van Horn local oreCount = 0 local blips = {} local Utils = {} local actions = {} actions["GoToMine"] = function(job) local blip = CreateBlip(mineEntrance, lang["Go to mine"], 'blip_objective', `BLIP_STYLE_OBJECTIVE`) table.insert(blips, blip) TriggerEvent("murphy_jobpanel:ShowObjective", lang["Go to mine"], 4000) StartGpsMultiRoute(GetHashKey("COLOR_OBJECTIVE"), true, true) Citizen.InvokeNative(0x64C59DD6834FA942, mineEntrance) Citizen.InvokeNative(0x4426D65E029A4DC0, true) Citizen.CreateThread(function() while true do Wait(500) if #(GetEntityCoords(PlayerPedId()) - mineEntrance) < 5.0 then ClearGpsMultiRoute() RemoveBlip(blip) job:nextStep() break end end end) Citizen.CreateThread(function() local step = job.currentStep while true do Wait(1000) if step ~= job.currentStep or job.abandon == true then RemoveBlip(blip) if job.abandon == true then ClearGpsMultiRoute() Utils.StopJob() end break end end end) end actions["MineOre"] = function(job) local data = job.steps[job.currentStep] local coords = data.coords TriggerEvent("murphy_jobpanel:ShowObjective", data.description, 4000) local blip = CreateBlip(coords, lang["Mine ore"], 'blip_objective', `BLIP_STYLE_OBJECTIVE`) table.insert(blips, blip) exports.murphy_interact:AddInteraction({ coords = vector3(coords.x, coords.y, coords.z + 1.0), distance = 8.0, interactDst = 2.0, id = job.name .. "_step" .. job.currentStep, options = { { label = lang["Mine ore"], action = function(entity, crds, args) local ped = PlayerPedId() local startPos = GetEntityCoords(ped) -- Play pickaxe animation LoadAnim("amb_work@world_human_pickaxe@male_a@idle_a") TaskPlayAnim(ped, ``` -------------------------------- ### Select Job Types in Lua Source: https://docs.murphy-workshop.com/murphy-s-job-board/features/job-types Example of defining job objects for hunting and delivery types in Lua. Note that available jobs may vary based on server configuration and active missions. ```lua -- Selecting a job type local huntingJob = { name = "Forest Deer Hunt", type = "hunting", difficulty = "medium", reward = 50 } local deliveryJob = { name = "Supply Wagon Delivery", type = "delivery", distance = "long", reward = 75 } ``` -------------------------------- ### Configure QBCore Framework Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/framework-support Set the framework to 'qb' and enable QBCore integration. ```lua -- Recommended QB setup Config.Framework = 'qb' Config.UseQBCore = true ``` -------------------------------- ### Configure VORP Framework Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/framework-support Set the framework to 'vorp' and enable VORP integration. ```lua -- Recommended VORP setup Config.Framework = 'vorp' Config.UseVORP = true ``` -------------------------------- ### Configure RedEM Framework Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/framework-support Set the framework to 'redem' and enable RedEM integration. ```lua -- Recommended RedEM setup Config.Framework = 'redem' Config.UseRedEM = true ``` -------------------------------- ### Configure Framework in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job-board/getting-started/configuration Select the server's core framework by modifying the Config.framework setting. Supported options include rsg-core, qbr-core, REDEMRP2k23, redemrp2022, and vorp. ```lua Config.framework = "rsg-core" -- Supported options: -- qbr-core -- REDEMRP2k23 -- redemrp2022 -- rsg-core -- vorp ``` -------------------------------- ### Enable Lockpick System Source: https://docs.murphy-workshop.com/murphy-s-job/reference/troubleshooting Configure the lockpick system by enabling it and verifying the item name matches your inventory. ```lua Config.Lockpick = true -- Must be true to enable Config.LockpickItem = "lockpick" -- Verify item name matches your inventory ``` -------------------------------- ### Configure RSG Framework Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/framework-support Set the framework to 'rsg' and enable RSG integration. ```lua -- Recommended RSG setup Config.Framework = 'rsg' Config.UseRSG = true ``` -------------------------------- ### Configure Config.framework in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job/advanced/framework-support Set the Config.framework variable to match your server's core framework. Supported values include "qbr-core", "REDEMRP2k23", "redemrp2022", "rsg-core", and "vorp". ```lua -- Possible values: -- "qbr-core" -- "REDEMRP2k23" -- "redemrp2022" -- "rsg-core" -- "vorp" Config.framework = "REDEMRP2k23" ``` -------------------------------- ### Set Framework Configuration Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-beginner-guide Update Config.framework in config/config.lua to match your server framework. ```lua Config.framework = "vorp" -- Options: vorp, rsg-core, REDEMRP2k23, redemrp2022, qbr-core ``` -------------------------------- ### Add resource to server.cfg Source: https://docs.murphy-workshop.com/murphy-s-job/getting-started/installation Ensure the required dependencies and the murphy_job resource are loaded by adding them to your server.cfg file. ```cfg ensure ox_lib ensure mysql-async ensure uiprompt ensure murphy_job ``` -------------------------------- ### Add resources to server.cfg Source: https://docs.murphy-workshop.com/murphy-s-job-board/getting-started/installation Add these lines to your server.cfg file to ensure the required dependencies and the job board resource are loaded. ```cfg ensure mysql-async ensure oxmysql ensure ox_lib ensure murphy_jobpanel ``` -------------------------------- ### Set Framework in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job/reference/troubleshooting Ensure the framework configuration matches your server's core framework. ```lua Config.framework = "REDEMRP2k23" -- Must match your server's core framework ``` -------------------------------- ### Define Job Configuration in config/jobs/my_first_job.lua Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-beginner-guide This template defines the job metadata, steps, and interaction logic. It handles both server-side registration and client-side execution of job actions like animations and completion. ```lua local JobData = { name = filename(), label = "Street Sweeper", description = "Clean the streets of Armadillo", steps = { { action = "PlayAnimation" }, { action = "PlayAnimation" }, { action = "CompleteJob", reward = 30, label = "Complete", description = "Collect your pay" }, }, board = { "armadillo" }, image = "./img/Poster_art/train2.png", reward = "$30", } if IsDuplicityVersion() then JobsConfig[filename()] = JobData else local lang = { ["NextStep"] = "Next step", ["Finish"] = "Job complete!", } local orderedLocations = { { name = "Sweep the porch", description = "Sweep in front of the general store", coords = vector3(-3626.562, -2605.512, -14.346), anim = "WORLD_HUMAN_BROOM", duration = 8000 }, { name = "Clean the window", description = "Wipe the bank window clean", coords = vector3(-3670.741, -2622.23, -14.752), anim = { dict = "amb_work@world_human_clean_window@male_a@wip_base", anim = "wip_base" }, duration = 8000 }, } local blips = {} local Utils = {} local actions = {} actions["PlayAnimation"] = function(job) local data = job.steps[job.currentStep] local coords = data.coords local label = data.label local description = data.description local anim = data.anim local duration = data.duration TriggerEvent("murphy_jobpanel:ShowObjective", description, 4000) local blip = CreateBlip(coords, label, 'blip_objective', `BLIP_STYLE_OBJECTIVE`) table.insert(blips, blip) exports.murphy_interact:AddInteraction({ coords = vector3(coords.x, coords.y, coords.z + 1.5), distance = 8.0, interactDst = 2.5, id = job.name .. "_step" .. job.currentStep, options = { { label = label, action = function(entity, coords, args) local playerPed = PlayerPedId() local startCoords = GetEntityCoords(playerPed) if type(anim) == "string" then TaskStartScenarioInPlace(playerPed, anim, -1, true) elseif type(anim) == "table" then LoadAnim(anim.dict) TaskPlayAnim(playerPed, anim.dict, anim.anim, 8.0, 8.0, -1, 25, 0, 0, 0, 0) end Citizen.Wait(duration) if #(GetEntityCoords(playerPed) - startCoords) > 10.0 then ClearPedTasks(playerPed) TriggerEvent("murphy_jobpanel:ShowObjective", "You moved too far, try again.", 4000) else ClearPedTasks(playerPed) exports.murphy_interact:RemoveInteraction(job.name .. "_step" .. job.currentStep) RemoveBlip(blip) job:nextStep() end end, }, } }) end actions["CompleteJob"] = function(job) TriggerEvent("murphy_jobpanel:ShowObjective", lang["Finish"], 4000) Callback.triggerServer('AddCurrency', function() end, job.steps[job.currentStep].reward) job:nextStep() Utils.StopJob() end function Utils.StopJob() for _, blip in ipairs(blips) do if blip then RemoveBlip(blip) end end blips = {} end function Utils.createJob(jobData) for i, step in ipairs(jobData.steps) do if step.action == "PlayAnimation" then local location = orderedLocations[i] step.coords = location.coords step.anim = location.anim step.duration = location.duration step.description = location.description step.label = location.name end step.action = actions[step.action] end return Job:new(jobData) end local job = Utils.createJob(JobData) AddEventHandler("onResourceStop", function(resourceName) if GetCurrentResourceName() ~= resourceName then return end Utils.StopJob() end) return job end ``` -------------------------------- ### Set Framework in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job/getting-started/configuration Define the framework used by the plugin in the config.lua file. Supported frameworks include qbr-core, REDEMRP2k23, redemrp2022, rsg-core, and vorp. ```lua Config.framework = "REDEMRP2k23" ``` -------------------------------- ### Configure framework in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job/getting-started/installation Set the framework type in the config.lua file to match your server environment. ```lua Config.framework = "REDEMRP2k23" -- Select your framework ``` -------------------------------- ### Configure Global Prompts Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-beginner-guide Edit the config/translate.lua file to update global job panel prompts. ```lua Translate = { ["Job Panel: View Jobs"] = "View Jobs", ["Already have a job"] = "You already have a job", ["Abandon"] = "You have abandoned the job", } ``` -------------------------------- ### Create Ordered Job Pattern Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Implementation of sequential location assignment for job steps. ```lua function Utils.createJob(jobData) local deliveryIndex = 1 for i, step in ipairs(jobData.steps) do if step.action == "DeliverPackage" then local location = deliveries[deliveryIndex] step.coords = location.Pos step.description = location.Description deliveryIndex = deliveryIndex + 1 end step.action = actions[step.action] end return Job:new(jobData) end ``` -------------------------------- ### Register Client Callback Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/callbacks Registers a callback on the client side using Callback.register(). ```lua Callback.register('ClientSpecificTask', function(data) -- Perform client-side logic return true end) ``` -------------------------------- ### Configure Delivery Settings in Lua Source: https://docs.murphy-workshop.com/murphy-s-job/features/delivery Defines the interaction key and translation strings for the delivery job system. ```lua Config.Delivery = { KeyToPick = 0xC7B5340A, -- Interaction key Translate = { VehBlip = "Work Carriage", NextStep = "Deliver Package to", Take = "Take the package", Drop = "Drop the package", Finish = "Give back the carriage" } } ``` -------------------------------- ### Configure Job Boards in config/config.lua Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-beginner-guide Define job board locations and models within the Config.Panel table. Use a unique key for each board and specify the prop model, world coordinates, and rotation. ```lua Config.Panel = { ["my_board_name"] = { model = "mp005_p_mp_bountyboard01x", -- Board prop model coords = vector3(x, y, z), -- World position rotation = vector3(0.0, 0.0, 90.0) -- Rotation (usually just Z axis) }, } ``` -------------------------------- ### Check player item amount via GetItemAmount Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Uses a server callback to verify if a player possesses a specific quantity of an item. ```lua Callback.triggerServer('GetItemAmount', function(amount) if amount >= requiredAmount then -- Player has enough end end, "item_name") ``` -------------------------------- ### Define Job Settings in jobs.lua Source: https://docs.murphy-workshop.com/murphy-s-job/getting-started/configuration Configure individual job details such as menu locations, duty systems, and grade-specific permissions in the jobs.lua file. ```lua ["tabac_rhodes"] = { config = "tabac", -- Job configuration file blisprite = 552659337, -- Map blip icon MenuLocations = vector3(1128.473, -975.7766, 67.83601), -- Job menu coordinates Name = "Tabac Anderson & Co", -- Job name Duty = true, -- Enable/disable duty system Grades = { [1] = { Name = "Assistant", StorageAccess = true, }, [2] = { Name = "Second", StorageAccess = true, }, [3] = { Name = "Boss", Personnel = true, -- Staff management StorageAccess = true, Pay = 0.5 -- Salary multiplier } } } ``` -------------------------------- ### Restart the Resource Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-beginner-guide Command to ensure the job panel resource is active. ```text ensure murphy_jobpanel ``` -------------------------------- ### Implement MineOre and DeliverOre Job Actions Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Defines the logic for mining and delivery steps, including interaction triggers, objective blips, and GPS routing. ```lua "amb_work@world_human_pickaxe@male_a@idle_a", "idle_a", 8.0, 8.0, -1, 25, 0, 0, 0, 0 ) Wait(8000) if #(GetEntityCoords(ped) - startPos) > 5.0 then ClearPedTasks(ped) TriggerEvent("murphy_jobpanel:ShowObjective", "You moved too far!", 4000) else ClearPedTasks(ped) oreCount = oreCount + 1 exports.murphy_interact:RemoveInteraction( job.name .. "_step" .. job.currentStep ) RemoveBlip(blip) job:nextStep() end end, }, } }) Citizen.CreateThread(function() local step = job.currentStep while true do Wait(1000) if step ~= job.currentStep or job.abandon == true then RemoveBlip(blip) if job.abandon == true then exports.murphy_interact:RemoveInteraction( job.name .. "_step" .. step ) Utils.StopJob() end break end end end) end actions["DeliverOre"] = function(job) TriggerEvent("murphy_jobpanel:ShowObjective", lang["Deliver"], 4000) local blip = CreateBlip(deliveryPoint, lang["Deliver"], 'blip_objective', `BLIP_STYLE_OBJECTIVE`) table.insert(blips, blip) StartGpsMultiRoute(GetHashKey("COLOR_OBJECTIVE"), true, true) Citizen.InvokeNative(0x64C59DD6834FA942, deliveryPoint) Citizen.InvokeNative(0x4426D65E029A4DC0, true) exports.murphy_interact:AddInteraction({ coords = vector3(deliveryPoint.x, deliveryPoint.y, deliveryPoint.z + 1.0), distance = 10.0, interactDst = 3.0, id = job.name .. "_step" .. job.currentStep, options = { { label = lang["Complete"], action = function() Callback.triggerServer('AddCurrency', function() exports.murphy_interact:RemoveInteraction( job.name .. "_step" .. job.currentStep ) job:nextStep() Utils.StopJob() end, job.steps[job.currentStep].reward) end, }, } }) Citizen.CreateThread(function() local step = job.currentStep while true do Wait(1000) if step ~= job.currentStep or job.abandon == true then RemoveBlip(blip) if job.abandon == true then exports.murphy_interact:RemoveInteraction( job.name .. "_step" .. step ) ClearGpsMultiRoute() Utils.StopJob() end break end end end) end ``` -------------------------------- ### Register Server Callback Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/callbacks Registers a callback on the server side using Callback.register(). ```lua Callback.register('GetCharIdentifier', function(source) -- Return character identifier for player return MySQL.query('SELECT character_id FROM characters WHERE player_id = ?', {source}) end) ``` -------------------------------- ### Configure Primary Language Source: https://docs.murphy-workshop.com/murphy-s-job-board/reference/translation Modify the configuration file to set the server's primary language using the appropriate language code. ```lua Config.Language = 'en' -- Change 'en' to your preferred language code ``` -------------------------------- ### Trigger Client to Server Callback Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Use Callback.triggerServer to send a request from the client to the server and handle the returned result. ```lua Callback.triggerServer('CallbackName', function(result) -- Handle the result print("Server returned:", result) end, arg1, arg2, ...) ``` -------------------------------- ### Callback.triggerServer('GiveItem') Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Adds a specified quantity of an item to the player's inventory. ```APIDOC ## Callback.triggerServer('GiveItem') ### Description Adds a specific amount of an item to the player's inventory. ### Parameters - **callback** (function) - Required - Function to handle the success boolean. - **item_name** (string) - Required - The identifier of the item to add. - **amount** (number) - Required - The quantity to add. - **metadata** (table) - Required - Metadata associated with the item. ``` -------------------------------- ### Create murphy_jobpanel database table Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide The schema required for tracking job progression, including character identifiers, job names, and current steps. ```sql CREATE TABLE `murphy_jobpanel` ( `id` INT(11) PRIMARY KEY AUTO_INCREMENT, `charid` VARCHAR(200) DEFAULT 0, `job` VARCHAR(2000), `step` VARCHAR(200) ); ``` -------------------------------- ### Callback.triggerServer('GetItemAmount') Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Checks the quantity of a specific item currently held by the player. ```APIDOC ## Callback.triggerServer('GetItemAmount') ### Description Retrieves the amount of a specific item held by the player. ### Parameters - **callback** (function) - Required - Function to handle the returned amount. - **item_name** (string) - Required - The identifier of the item to check. ``` -------------------------------- ### Configure Delivery Mission Settings in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job/features/quests Defines the interaction key, objective color, and translation strings for delivery missions. ```lua Config.Delivery = { KeyToPick = 0xC7B5340A, -- Interaction key ObjectiveColor = GetHashKey("COLOR_OBJECTIVE"), Translate = { VehBlip = "Work Carriage", NextStep = "Deliver Package to", Take = "Take the package", Drop = "Drop the package" } } ``` -------------------------------- ### Trigger Client to Server Callback Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/callbacks Triggers a callback from the client to the server using Callback.triggerServer(). ```lua -- Trigger callback to server Callback.triggerServer('ServerEventName', function(result) print(result) -- Handle server response end) ``` -------------------------------- ### Configure aggressive animal relationships Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Sets up a relationship group where animals are hostile toward the player and disables fleeing behaviors. ```lua AddRelationshipGroup("animalstokill") SetRelationshipBetweenGroups(5, GetHashKey("animalstokill"), GetHashKey("PLAYER")) -- 5 = Hate SetRelationshipBetweenGroups(5, GetHashKey("PLAYER"), GetHashKey("animalstokill")) -- Assign to spawned animal: SetPedRelationshipGroupHash(animal, GetHashKey("animalstokill")) SetPedCombatAttributes(animal, 58, true) -- CA_DISABLE_FLEE_FROM_COMBAT -- Prevent fleeing behavior: Citizen.InvokeNative(0x9FF1E042FA597187, animal, 22, 0) -- ATB_FleeMountedPeds = false Citizen.InvokeNative(0x9FF1E042FA597187, animal, 88, 1) -- ATB_WaitInsteadOfFleeForUnreachableVolumes = true ``` -------------------------------- ### Configure Job Grades and Salaries in Lua Source: https://docs.murphy-workshop.com/murphy-s-job/features/job-system Defines the job structure and salary tiers for a delivery job within the configuration file. ```lua -- Sample job configuration JobsRoute = { delivery = { grades = { ["0"] = { name = "Rookie", salary = 50 }, ["1"] = { name = "Experienced", salary = 75 }, ["2"] = { name = "Senior", salary = 100 } } } } ``` -------------------------------- ### Create Random Job Pattern Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Implementation of randomized location assignment with deduplication. Ensure the deliveries table is larger than the number of steps to prevent infinite loops. ```lua function Utils.createJob(jobData) local usedLocations = {} for _, step in ipairs(jobData.steps) do if step.action == "DeliverPackage" then local location repeat location = deliveries[math.random(#deliveries)] until not usedLocations[location] usedLocations[location] = true step.coords = location.Pos step.description = location.Description end step.action = actions[step.action] end return Job:new(jobData) end ``` -------------------------------- ### Give items via GiveItem Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Triggers a server callback to grant a specified amount of an item to the player. ```lua Callback.triggerServer('GiveItem', function(success) if success then -- Item given end end, "item_name", amount, {}) ``` -------------------------------- ### Define Language File Structure Source: https://docs.murphy-workshop.com/murphy-s-job/reference/translations Language files use a key-value format within a Translations table. ```lua Translations = { ['job_title'] = 'Worker', ['start_work'] = 'Start Work', ['stop_work'] = 'End Shift' } ``` -------------------------------- ### Database State Management Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide SQL queries to inspect active jobs and reset stuck player records in the murphy_jobpanel table. ```sql -- See all active jobs: SELECT * FROM murphy_jobpanel WHERE job IS NOT NULL; -- Clear a stuck player: UPDATE murphy_jobpanel SET job = NULL, step = NULL WHERE charid = 'PLAYER_CHARID'; ``` -------------------------------- ### Define In-Game Job Translations Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-beginner-guide Configure the lang table within individual job files to translate step-specific text. ```lua local lang = { ["Take"] = "Take", ["Drop"] = "Drop", ["Finish"] = "Finish", } ``` -------------------------------- ### Define Job Utilities and Resource Cleanup Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Provides helper functions for stopping jobs, creating job instances, and handling resource termination. ```lua function Utils.StopJob() ClearGpsMultiRoute() for _, blip in ipairs(blips) do if blip then RemoveBlip(blip) end end blips = {} oreCount = 0 end function Utils.createJob(jobData) local oreIndex = 1 for i, step in ipairs(jobData.steps) do if step.action == "MineOre" then local spot = oreSpots[oreIndex] step.coords = spot.coords step.description = spot.description oreIndex = oreIndex + 1 end step.action = actions[step.action] end return Job:new(jobData) end local job = Utils.createJob(JobData) AddEventHandler("onResourceStop", function(resourceName) if GetCurrentResourceName() ~= resourceName then return end Utils.StopJob() end) return job ``` -------------------------------- ### Define Job Board Locations in Config.Panel Source: https://docs.murphy-workshop.com/murphy-s-job-board/getting-started/configuration Add job board locations to the Config.Panel table. Each entry requires a unique identifier, a model, vector3 coordinates, and rotation. ```lua Config.Panel = { ["armadillo"] = { model = "mp005_p_mp_bountyboard01x", coords = vector3(-3727.36, -2598.500244140625, -14.46000003814697), rotation = vector3(0.0,0.0,89) }, -- Add more locations as needed } ``` -------------------------------- ### Check Callback Registration Source: https://docs.murphy-workshop.com/murphy-s-job-board/reference/troubleshooting Verify if a specific callback has been registered by checking the registeredCallback table. ```lua -- Example callback registration check if not registeredCallback[name] then print('No callback registered for:', name) end ``` -------------------------------- ### Trigger Server to Client Callback Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Use Callback.triggerClient to send a request from the server to a specific client and handle the returned result. ```lua Callback.triggerClient('CallbackName', source, function(result) print("Client returned:", result) end, arg1, arg2, ...) ``` -------------------------------- ### Trigger Server to Client Callback Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/callbacks Triggers a callback from the server to a specific client using Callback.triggerClient(). ```lua -- Trigger callback to specific client Callback.triggerClient('EventName', playerSource, function(result) print(result) -- Handle client response end) ``` -------------------------------- ### Job System Architecture Diagram Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Visual representation of the job file structure, showing the split between shared JobData and the separation of server-side configuration and client-side gameplay logic. ```text ┌─────────────────────────────────────────────────────────────┐ │ Job File (config/jobs/xxx.lua) │ │ │ │ ┌─────────────────────┐ ┌─────────────────────────────┐ │ │ │ JobData (shared) │ │ IsDuplicityVersion() split │ │ │ │ name, label, steps │───>│ Server: JobsConfig[name] │ │ │ │ board, image, reward│ │ Client: actions + Job:new() │ │ │ └─────────────────────┘ └─────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ JobManager │ │ Callbacks │ │ Adapters │ │ (client) │ │ (shared) │ │ (server-side)│ │ │ │ │ │ │ │ Job:new() │ │ triggerSvr │ │ AddCurrency │ │ Job:start()│ │ triggerCli │ │ GiveItem │ │ Job:stop() │ │ register() │ │ RemoveItem │ │ nextStep() │ │ │ │ GetCharJob │ └────────────┘ └────────────┘ └──────────────┘ ``` -------------------------------- ### Utils Pattern Structure Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide The standard organizational pattern for job files, requiring a Utils table with StopJob, createJob, and an onResourceStop handler. ```lua local Utils = {} local actions = {} -- Action definitions actions["StepName"] = function(job) ... end actions["CompleteJob"] = function(job) ... end -- Cleanup function (REQUIRED) function Utils.StopJob() -- Delete all spawned entities -- Remove all blips -- Clear GPS routes -- Reset local state variables end -- Job factory (REQUIRED) function Utils.createJob(jobData) for i, step in ipairs(jobData.steps) do -- Map data from location tables to steps -- Convert string action names to function references step.action = actions[step.action] end return Job:new(jobData) end -- Create and return the job instance local job = Utils.createJob(JobData) -- Resource cleanup handler (REQUIRED) AddEventHandler("onResourceStop", function(resourceName) if GetCurrentResourceName() ~= resourceName then return end Utils.StopJob() end) return job ``` -------------------------------- ### Define Job Board Locations in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-beginner-guide The Config.Panel table defines the available physical job boards in the world. Use this configuration to map specific job boards to the board field in your job files. ```lua Config.Panel = { ["armadillo"] = { ... }, ["vanhorn"] = { ... }, ["oil"] = { ... }, } ``` -------------------------------- ### Create Map Blips Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Functions for creating standard, radius, and entity-attached blips on the map. ```lua -- Standard blip at coordinates local blip = CreateBlip(coords, "Label", 'blip_objective', `BLIP_STYLE_OBJECTIVE`) -- Radius blip (circular area on map) local blip = CreateBlipRadius(coords, radius, "Label", 'blip_objective', `BLIP_STYLE_OBJECTIVE`) -- Entity blip (follows a ped/vehicle) local blip = CreateBlipEntity(entity, "Label", `BLIP_STYLE_OBJECTIVE`, nil, `blip_ambient_ped_small`) ``` -------------------------------- ### Callback.triggerServer(name, callback) Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/callbacks Triggers a registered callback on the server from a client. ```APIDOC ## Callback.triggerServer(name, callback) ### Description Sends a request to the server to execute a registered callback. ### Parameters - **name** (string) - The name of the registered callback. - **callback** (function) - The response handler function to execute once the server returns a result. ``` -------------------------------- ### Customize Stash Options in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job/getting-started/configuration Adjust stash weight, lockpicking functionality, and associated item requirements in the config.lua file. ```lua Config.StashWeight = 10000000.0 -- Total stash weight Config.Lockpick = true -- Enable lockpicking Config.LockpickItem = "lockpick" -- Item required for lockpicking Config.LockpickDelay = 600 -- Stash open duration after lockpick (seconds) ``` -------------------------------- ### Update configuration settings in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job/getting-started/installation Adjust job-specific settings such as stash weight, salary, and lockpicking options. ```lua Config.StashWeight = 10000000.0 -- Job stash total weight Config.Salary = true -- Enable salary (requires murphy_banking) Config.SalaryTimer = 120 -- Salary interval in seconds Config.Lockpick = true -- Enable lockpicking system Config.LockpickItem = "lockpick" -- Item required for lockpicking ``` -------------------------------- ### Configure UI Language Text Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-beginner-guide Modify ui/language_config.json to translate UI buttons and headers. ```json [ { "Close_button": "Close", "Accept_button": "Accept" }, { "Header_title": "ACTIVE JOB", "Objectives_title": "Objectives", "Reward_text": "Reward", "Abandon_button": "Abandon" } ] ``` -------------------------------- ### Draw GPS Route Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Commands to initiate a multi-point GPS route on the minimap and clear it when finished. ```lua -- Start route StartGpsMultiRoute(GetHashKey("COLOR_OBJECTIVE"), true, true) Citizen.InvokeNative(0x64C59DD6834FA942, destination) -- Add waypoint Citizen.InvokeNative(0x4426D65E029A4DC0, true) -- Render route -- Clear route ClearGpsMultiRoute() ``` -------------------------------- ### Callback.register(name, callback) Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/callbacks Registers a callback function on either the server or client side to handle incoming requests. ```APIDOC ## Callback.register(name, callback) ### Description Registers a handler function for a specific callback name. This allows the system to execute logic when the callback is triggered. ### Parameters - **name** (string) - The unique identifier for the callback. - **callback** (function) - The function to execute when triggered. Receives data/source as arguments and returns a result. ``` -------------------------------- ### Debug Print Statements Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Use these print statements to track job progress and entity spawning in the console. ```lua print("Step", job.currentStep, "coords:", data.coords) print("Entity spawned:", entity, "at", GetEntityCoords(entity)) ``` -------------------------------- ### Configure Stash Lockpicking in config.lua Source: https://docs.murphy-workshop.com/murphy-s-job/advanced/stash-system Set the lockpicking status, the required item for the action, and the duration the stash remains open after a successful crack. ```lua Config.Lockpick = true Config.LockpickItem = "lockpick" Config.LockpickDelay = 600 ``` -------------------------------- ### Configure passive animal relationships Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Sets up a neutral relationship group for herding, disables specific flee behaviors, and enables movement along the road navmesh. ```lua AddRelationshipGroup("herdanimals") SetRelationshipBetweenGroups(1, GetHashKey("herdanimals"), GetHashKey("PLAYER")) -- 1 = Respect SetRelationshipBetweenGroups(1, GetHashKey("PLAYER"), GetHashKey("herdanimals")) -- Configure cow behavior: SetAnimalTuningBoolParam(cow, 25, false) -- Disable specific flee behavior SetAnimalTuningBoolParam(cow, 24, false) TaskAnimalUnalerted(cow, -1, false, 0, 0) SetPedConfigFlag(cow, 297, true) SetPedConfigFlag(cow, 310, true) SetPedConfigFlag(cow, 40, false) -- Disable mounting and lassoing: Citizen.InvokeNative(0xAE6004120C18DF97, cow, 5, false) Citizen.InvokeNative(0xAE6004120C18DF97, cow, 6, false) -- Move cow toward destination via road navmesh: TaskMoveFollowRoadUsingNavmesh(cow, 2.0, dest.x, dest.y, dest.z, 0) ``` -------------------------------- ### Callback.triggerServer Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Triggers a server-side callback from the client. ```APIDOC ## Callback.triggerServer ### Description Triggers a pre-registered server-side callback from the client. ### Signature `Callback.triggerServer(CallbackName, callbackFunction, ...args)` ### Parameters - **CallbackName** (string) - The name of the registered server callback. - **callbackFunction** (function) - The function to execute once the server returns a result. - **...args** (any) - Variable arguments to pass to the server callback. ``` -------------------------------- ### Guard framework-specific adapter code Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Use this conditional check to isolate framework-specific implementations within the adapter layer. ```lua if Config.framework == "vorp" then -- VORP-specific implementation end ``` -------------------------------- ### Spawn a Vehicle Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Spawns a vehicle using its hash key. Always call SetEntityAsMissionEntity to prevent the game from despawning the vehicle. ```lua local vehicleHash = GetHashKey("chuckwagon002x") RequestModel(vehicleHash) while not HasModelLoaded(vehicleHash) do Wait(1) end local vehicle = CreateVehicle( vehicleHash, coords.x, coords.y, coords.z, heading, -- float: spawn rotation true, true, -- isNetwork, thisScriptCheck false, true -- p6, p7 ) SetEntityAsMissionEntity(vehicle, true, true) ``` -------------------------------- ### Clean Up Blips Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/murphy-jobpanel-advanced-guide Pattern for tracking active blips in a table and removing them during job termination. ```lua local blips = {} -- When creating: table.insert(blips, blip) -- In StopJob: for _, blip in ipairs(blips) do if blip then RemoveBlip(blip) end end blips = {} ``` -------------------------------- ### Callback.triggerClient(name, playerSource, callback) Source: https://docs.murphy-workshop.com/murphy-s-job-board/advanced/callbacks Triggers a registered callback on a specific client from the server. ```APIDOC ## Callback.triggerClient(name, playerSource, callback) ### Description Sends a request to a specific client to execute a registered callback. ### Parameters - **name** (string) - The name of the registered callback. - **playerSource** (number) - The ID of the target client. - **callback** (function) - The response handler function to execute once the client returns a result. ```