From b5236ffd862327ff0da23c8e7f5f85c81550a852 Mon Sep 17 00:00:00 2001 From: Lation Date: Mon, 24 Jul 2023 13:50:14 -0400 Subject: [PATCH] v1.1.0 release - Added bridge for multi-framework support - Supports ESX & QBCore now - Added automatic framework detection for ESX/QB - Lots of code refactoring - Added new drug effects (speed & screen effects) - New drug effects completely customizable - Each effect can be individually toggled - Now supports qb-target & qtarget --- .vscode/settings.json | 12 ++ README.md | 11 +- bridge/client.lua | 67 +++++++++++ bridge/server.lua | 88 ++++++++++++++ client/client.lua | 262 ++++++++++++++++++++++-------------------- config.lua | 27 +++-- fxmanifest.lua | 7 +- server/server.lua | 122 +++++++++++--------- 8 files changed, 405 insertions(+), 191 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 bridge/client.lua create mode 100644 bridge/server.lua diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..dfcbe4f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "Lua.diagnostics.globals": [ + "lib", + "sendToDiscord", + "ESX", + "result", + "spawnStartOxyRunPed", + "startOxyRunDialog", + "startOxyRunPed", + "cache" + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 9d7702f..52a3e53 100644 --- a/README.md +++ b/README.md @@ -12,19 +12,22 @@ You can open the bottle of oxy and in turn receive individual pills. With those [Click here to join our Discord](https://discord.gg/9EbY4nM5uu) ## Features +- Supports ESX & QBCore +- Highly detailed config file - Set as many (or as little) Pharmacy locations you want - Create a list of as many (or as little) Doctor names - Enable/disable Discord webhook logs -- Enable/disable health or armor effects upon use +- Enable/disable health effects upon oxycontin use +- Enable/disable armor effects upon oxycontin use +- Enable/disable "speed boost" effects upon oxycontin use +- Enable/disable screen effects upon oxycontin use - Set random pricing when purchasing a blank script, or a fixed price - Optimized 0.00 on use & idle -- Only supports ESX / easy to convert -- Highly detailed config file ## Dependencies - [ox_inventory](https://github.com/overextended/ox_inventory/releases) - [ox_lib](https://github.com/overextended/ox_lib/releases) -- [ox_target](https://github.com/overextended/ox_target/releases) +- [ox_target](https://github.com/overextended/ox_target/releases), [qb-target](https://github.com/qbcore-framework/qb-target) or [qtarget](https://github.com/overextended/ox_target/releases) ## Installation - Ensure you have all dependencies installed diff --git a/bridge/client.lua b/bridge/client.lua new file mode 100644 index 0000000..5f5aa09 --- /dev/null +++ b/bridge/client.lua @@ -0,0 +1,67 @@ +PlayerLoaded, PlayerData = nil, {} + +local libState = GetResourceState('ox_lib') + +-- Get framework +if GetResourceState('es_extended') == 'started' then + ESX = exports['es_extended']:getSharedObject() + Framework = 'esx' + + RegisterNetEvent('esx:onPlayerLogout', function() + table.wipe(PlayerData) + PlayerLoaded = false + end) + + AddEventHandler('onResourceStart', function(resourceName) + if GetCurrentResourceName() ~= resourceName or not ESX.PlayerLoaded then + return + end + PlayerData = ESX.GetPlayerData() + PlayerLoaded = true + end) + +elseif GetResourceState('qb-core') == 'started' then + QBCore = exports['qb-core']:GetCoreObject() + Framework = 'qb' + + AddStateBagChangeHandler('isLoggedIn', '', function(_bagName, _key, value, _reserved, _replicated) + if value then + PlayerData = QBCore.Functions.GetPlayerData() + else + table.wipe(PlayerData) + end + PlayerLoaded = value + end) + + AddEventHandler('onResourceStart', function(resourceName) + if GetCurrentResourceName() ~= resourceName or not LocalPlayer.state.isLoggedIn then + return + end + PlayerData = QBCore.Functions.GetPlayerData() + PlayerLoaded = true + end) +else + -- Add support for a custom framework here + return +end + +-- Use ox_lib notifications if ox_lib is found else use framework notifications +ShowNotification = function(title, message, type) + if libState == 'started' then + lib.notify({ + title = title, + description = message, + type = type, + icon = Notifications.icon, + position = Notifications.position + }) + else + if Framework == 'esx' then + ESX.ShowNotification(message) + elseif Framework == 'qb' then + QBCore.Functions.Notify(message, type) + else + + end + end +end \ No newline at end of file diff --git a/bridge/server.lua b/bridge/server.lua new file mode 100644 index 0000000..b171ae7 --- /dev/null +++ b/bridge/server.lua @@ -0,0 +1,88 @@ +-- Get framework +if GetResourceState('es_extended') == 'started' then + ESX = exports['es_extended']:getSharedObject() + Framework = 'esx' +elseif GetResourceState('qb-core') == 'started' then + QBCore = exports['qb-core']:GetCoreObject() + Framework = 'qb' +else + -- Add support for a custom framework here + return +end + +-- Get player from source +GetPlayer = function(source) + if Framework == 'esx' then + return ESX.GetPlayerFromId(source) + elseif Framework == 'qb' then + return QBCore.Functions.GetPlayer(source) + else + -- Add support for a custom framework here + end +end + +-- Get player's name from source +GetName = function(source) + local player = GetPlayer(source) + if player then + if Framework == 'esx' then + return player.getName() + elseif Framework == 'qb' then + return player.PlayerData.charinfo.firstname..' '..player.PlayerData.charinfo.lastname + end + end +end + +-- Get a players identifier +GetIdentifier = function(source) + local player = GetPlayer(source) + if player then + if Framework == 'esx' then + return player.identifier + elseif Framework == 'qb' then + return player.PlayerData.citizenid + end + end +end + +-- Function to register a usable item +RegisterUsableItem = function(item, ...) + if Framework == 'esx' then + ESX.RegisterUsableItem(item, ...) + elseif Framework == 'qb' then + QBCore.Functions.CreateUseableItem(item, ...) + else + -- Add support for a custom framework here + end +end + +RemoveMoney = function(source, moneyType, amount) + local player = GetPlayer(source) + moneyType = ConvertMoneyType(moneyType) + if player then + if Framework == 'esx' then + player.removeAccountMoney(moneyType, amount) + elseif Framework == 'qb' then + player.Functions.RemoveMoney(moneyType, amount) + end + end +end + +ConvertMoneyType = function(moneyType) + if moneyType == 'money' and Framework == 'qb' then + moneyType = 'cash' + elseif moneyType == 'cash' and Framework == 'esx' then + moneyType = 'money' + end + return moneyType +end + +GetPlayerAccountFunds = function(source, moneyType) + local player = GetPlayer(source) + moneyType = ConvertMoneyType(moneyType) + if Framework == 'qb' then + return player.PlayerData.money[moneyType] + elseif Framework == 'esx' then + return player.getAccount(moneyType).money + end +end \ No newline at end of file diff --git a/client/client.lua b/client/client.lua index efe94d1..f55e36a 100644 --- a/client/client.lua +++ b/client/client.lua @@ -1,61 +1,16 @@ -local ox_target = exports.ox_target +local qtarget = exports.qtarget local startOxyRun = lib.points.new(Config.StartOxyRunLocation, Config.StartOxyRunPedRadius) local missionActive = false local getPharmacies = math.random(1, #Config.PharmacyLocations) local selectPharmacy = Config.PharmacyLocations[getPharmacies] local doctorList = {} -local startOxyRunOptions = { - { - name = 'startOxyRun', - icon = Target.startOxyRunIcon, - label = Target.startOxyRunLabel, - distance = 2, - onSelect = function() - if Config.RequireItem then - local hasItem = lib.callback.await('lation_oxyrun:hasItem', source, Config.RequireItemName) - if hasItem >= Config.RequireItemAmount then - startOxyRunDialog() - else - lib.notify({ title = Notifications.startOxyRunPedName, description = Notifications.startOxyRunDidntHaveItemDescription, type = 'error', icon = Notifications.icon, position = Notifications.position }) - end - else - startOxyRunDialog() - end - end - } -} - -AddEventHandler('onResourceRestart', function(resource) - if resource == GetCurrentResourceName() then - missionActive = false - end -end) - --- Spawns the mission start ped when player enters the defined area -function startOxyRun:onEnter() - spawnStartOxyRunPed() - ox_target:addLocalEntity(startOxyRunPed, startOxyRunOptions) -end - --- Deletes the mission start ped when player exits the defined area -function startOxyRun:onExit() - DeleteEntity(startOxyRunPed) - ox_target:removeLocalEntity(startOxyRunPed, nil) -end - --- Function that handles the actual spawning of the ped, etc -function spawnStartOxyRunPed() - lib.RequestModel(Config.startOxyRunPedModel) - startOxyRunPed = CreatePed(0, Config.startOxyRunPedModel, Config.StartOxyRunLocation, Config.StartOxyRunPedHeading, false, true) - FreezeEntityPosition(startOxyRunPed, true) - SetBlockingOfNonTemporaryEvents(startOxyRunPed, true) - SetEntityInvincible(startOxyRunPed, true) - Entity(startOxyRunPed).state:set("sellDrugs", true, true) -- disables sell drugs option on peds -end +local perscriptionData = nil -- The initial dialog that is encountered when starting the mission function startOxyRunDialog() - if missionActive then return lib.notify({ title = Notifications.startOxyRunPedName, description = Notifications.startOxyRunAlreadyStarted, type = 'error', icon = Notifications.icon, position = Notifications.position }) end + if missionActive then + return ShowNotification(Notifications.startOxyRunPedName, Notifications.startOxyRunAlreadyStarted, 'error') + end local startOxyRunAlert = lib.alertDialog({ header = AlertDialog.startOxyRunHeader, content = AlertDialog.startOxyRunContent, @@ -67,49 +22,40 @@ function startOxyRunDialog() } }) if startOxyRunAlert == 'confirm' then + local blankPrescriptionPrice = nil if Config.RandomBlankPrescriptionPricing then - local blankPrescriptionPrice = math.random(Config.MinBlankPrescriptionPrice, Config.MaxBlankPrescriptionPrice) - local confirmStartOxyRun = lib.alertDialog({ - header = AlertDialog.startOxyRunPart2Header, - content = AlertDialog.startOxyRunPart2Content .. blankPrescriptionPrice ..' sound?', - centered = true, - cancel = true, - labels = { - confirm = AlertDialog.startOxyRunPart2Confirm, - cancel = AlertDialog.startOxyRunPart2Cancel - } - }) - if confirmStartOxyRun == 'confirm' then - lib.callback.await('lation_oxyrun:startOxyRun', source, blankPrescriptionPrice) - missionActive = true - else - lib.notify({ title = Notifications.startOxyRunPedName, description = Notifications.startOxyRunPart2CancelDescription, type = 'error', icon = Notifications.icon, position = Notifications.position }) - end + blankPrescriptionPrice = math.random(Config.MinBlankPrescriptionPrice, Config.MaxBlankPrescriptionPrice) else - local confirmStartOxyRunNotRandom = lib.alertDialog({ - header = AlertDialog.startOxyRunPart2Header, - content = AlertDialog.startOxyRunPart2Content .. Config.BlankPrescriptionPrice ..' sound?', - centered = true, - cancel = true, - labels = { - confirm = AlertDialog.startOxyRunPart2Confirm, - cancel = AlertDialog.startOxyRunPart2Cancel - } - }) - if confirmStartOxyRunNotRandom == 'confirm' then - lib.callback.await('lation_oxyrun:startOxyRun', source, Config.BlankPrescriptionPrice) + blankPrescriptionPrice = Config.BlankPrescriptionPrice + end + local confirmStartOxyRun = lib.alertDialog({ + header = AlertDialog.startOxyRunPart2Header, + content = AlertDialog.startOxyRunPart2Content .. blankPrescriptionPrice ..' sound?', + centered = true, + cancel = true, + labels = { + confirm = AlertDialog.startOxyRunPart2Confirm, + cancel = AlertDialog.startOxyRunPart2Cancel + } + }) + if confirmStartOxyRun == 'confirm' then + local success = lib.callback.await('lation_oxyrun:startOxyRun', false, blankPrescriptionPrice) + if success then missionActive = true else - lib.notify({ title = Notifications.startOxyRunPedName, description = Notifications.startOxyRunPart2CancelDescription, type = 'error', icon = Notifications.icon, position = Notifications.position }) + ShowNotification(Notifications.startOxyRunPedName, Notifications.notEnoughMoney, 'error') + missionActive = false end + else + ShowNotification(Notifications.startOxyRunPedName, Notifications.startOxyRunPart2CancelDescription, 'error') end else - lib.notify({ title = Notifications.startOxyRunPedName, description = Notifications.startOxyRunCancelDescription, type = 'error', icon = Notifications.icon, position = Notifications.position }) + ShowNotification(Notifications.startOxyRunPedName, Notifications.startOxyRunCancelDescription, 'error') end end -- The callback that displays an input dialog for the player to input information and returns it to the server -lib.callback.register('lation_oxyrun:fillPrescriptionInfo', function(source) +lib.callback.register('lation_oxyrun:fillPrescriptionInfo', function() local inputData = lib.inputDialog(InputDialog.header, { {type = 'input', label = InputDialog.nameLabel, description = InputDialog.nameDescription, icon = InputDialog.nameIcon, required = true, min = 5, max = 25}, {type = 'input', label = InputDialog.addressLabel, description = InputDialog.addressDescription, placeholder = InputDialog.addressPlaceholder, icon = InputDialog.addressIcon, required = true, min = 10, max = 40}, @@ -118,11 +64,13 @@ lib.callback.register('lation_oxyrun:fillPrescriptionInfo', function(source) {type = 'date', label = InputDialog.dobLabel, icon = InputDialog.dobIcon, default = true, format = InputDialog.dobFormat, required = true}, {type = 'input', label = InputDialog.doctorLabel, description = InputDialog.doctorDescription, icon = InputDialog.doctorIcon, required = true, min = 5, max = 25} }) + perscriptionData = inputData return inputData end) -- Alerts the player the script they are handing in is a fake -lib.callback.register('lation_oxyrun:fakeScript', function(source) +RegisterNetEvent('lation_oxyrun:fakeScript') +AddEventHandler('lation_oxyrun:fakeScript', function() lib.alertDialog({ header = AlertDialog.fakeScriptHeader, content = AlertDialog.fakeScriptContent, @@ -132,13 +80,12 @@ lib.callback.register('lation_oxyrun:fakeScript', function(source) end) -- Creates the target option at a randomly selected Pharymacy from the Config -ox_target:addSphereZone({ - coords = selectPharmacy, - radius = 1, - debug = false, +qtarget:AddCircleZone('randomPharmacy', selectPharmacy, 1.0, { + name = 'randomPharmacy1', + debugPoly = true, +}, { options = { { - name = 'randomPharmacy', icon = Target.fillScriptIcon, label = Target.fillScriptLabel, canInteract = function() @@ -148,8 +95,8 @@ ox_target:addSphereZone({ return false end end, - onSelect = function() - local hasItem = lib.callback.await('lation_oxyrun:hasItem', source, Config.SignedPerscription) + action = function() + local hasItem = lib.callback.await('lation_oxyrun:hasItem', false, Config.SignedPerscription) if hasItem >= 1 then if lib.progressCircle({ label = ProgressCircle.checkingScriptLabel, @@ -157,23 +104,20 @@ ox_target:addSphereZone({ position = ProgressCircle.checkingScriptPosition, useWhileDead = false, canCancel = true, - disable = { - car = true, - move = true, - combat = true, - } + disable = {car = true, move = true, combat = true} }) then - lib.callback.await('lation_oxyrun:getItemMetadata', source, item) + TriggerServerEvent('lation_oxyrun:getItemMetadata') missionActive = false else - lib.notify({ title = Notifications.pharmacyTitle, description = Notifications.pharmacyDescription, icon = Notifications.icon, type = 'error', position = Notifications.position }) + ShowNotification(Notifications.pharmacyTitle, Notifications.pharmacyDescription, 'error') end else - lib.notify({ title = Notifications.pharmacyTitle, description = Notifications.pharmacyItemNotFound, icon = Notifications.icon, type = 'error', position = Notifications.position }) + ShowNotification(Notifications.pharmacyTitle, Notifications.pharmacyItemNotFound, 'error') end end } - } + }, + distance = 2, }) -- Loops the available Doctor Names from the Config to display neatly in a menu @@ -185,16 +129,16 @@ for k, v in pairs(Config.DoctorNames) do end -- Target zone for opening and viewing the available Doctor Names -ox_target:addSphereZone({ - coords = Config.AvailableDoctorListLocation, - radius = 1, - debug = false, + +qtarget:AddCircleZone('availableDoctors', Config.AvailableDoctorListLocation, 1.0, { + name = 'availableDoctors1', + debugPoly = true, +}, { options = { { - name = 'availableDoctors', icon = Target.availableDoctorsIcon, label = Target.availableDoctors, - onSelect = function() + action = function() lib.registerContext({ id = 'availableDoctorsMenu', title = ContextMenu.availableDoctorsMenuTitle, @@ -203,12 +147,12 @@ ox_target:addSphereZone({ lib.showContext('availableDoctorsMenu') end } - } + }, + distance = 2, }) -- The event for opening the Oxy Bottle, returns if opened or not -lib.callback.register('lation_oxyrun:openOxyBottle', function(source, result) - local result = nil +lib.callback.register('lation_oxyrun:openOxyBottle', function() if lib.progressCircle({ label = ProgressCircle.openOxyBottleLabel, duration = ProgressCircle.openOxyBottleDuration, @@ -217,17 +161,15 @@ lib.callback.register('lation_oxyrun:openOxyBottle', function(source, result) canCancel = true, anim = { dict = 'missheistfbisetup1', clip = 'hassle_intro_loop_f', flag = 3 }, }) then - result = true - return result + return true else - result = false - return result + ShowNotification(Notifications.oxyBottleTitle, Notifications.oxyBottleDescription, 'error') + return false end end) -- The event for using/consuming an oxycontin, returns if opened or not -lib.callback.register('lation_oxyrun:useOxycontin', function(source, result) - local result = nil +lib.callback.register('lation_oxyrun:useOxycontin', function() if lib.progressCircle({ label = ProgressCircle.poppingOxyLabel, duration = ProgressCircle.poppingOxyDuration, @@ -236,20 +178,96 @@ lib.callback.register('lation_oxyrun:useOxycontin', function(source, result) canCancel = true, anim = { dict = 'mp_suicide', clip = 'pill' }, }) then - result = true - return result + return true else - result = false - return result + ShowNotification(Notifications.oxycontinTitle, Notifications.oxycontinDescription, 'error') + return false end end) --- The event if effects are enabled, this one is for setting health -lib.callback.register('lation_oxyrun:setPedHealth', function(source) +-- Callback to get prescription data +lib.callback.register('lation_oxyrun:getPrescriptionData', function() + return perscriptionData +end) + +-- Event used for applying health +RegisterNetEvent('lation_oxyrun:setPedHealth') +AddEventHandler('lation_oxyrun:setPedHealth', function() SetEntityHealth(cache.ped, Config.EnableEffects.health.amount or 0) end) --- The event if effects are enabled, this one is for setting armor -lib.callback.register('lation_oxyrun:setPedArmor', function(source) +-- Event used for applying armor +RegisterNetEvent('lation_oxyrun:setPedArmor') +AddEventHandler('lation_oxyrun:setPedArmor', function() SetPedArmour(cache.ped, Config.EnableEffects.armor.amount or 0) +end) + +-- Event that adds speed to the player +RegisterNetEvent('lation_oxyrun:applySpeed') +AddEventHandler('lation_oxyrun:applySpeed', function() + local multiplier = Config.EnableEffects.speed.multiplier + local duration = Config.EnableEffects.speed.duration * 1000 + SetRunSprintMultiplierForPlayer(cache.playerId, multiplier) + Citizen.SetTimeout(duration, function() + SetRunSprintMultiplierForPlayer(cache.playerId, 1.0) + end) +end) + +-- Event that adds screen effects to the player +RegisterNetEvent('lation_oxyrun:applyScreenEffect') +AddEventHandler('lation_oxyrun:applyScreenEffect', function() + SetTimecycleModifier(Config.EnableEffects.screenEffect.effect) + Citizen.SetTimeout(Config.EnableEffects.screenEffect.duration, function() + ClearTimecycleModifier() + end) +end) + +-- Spawns the mission start ped when player enters the defined area +function startOxyRun:onEnter() + spawnStartOxyRunPed() + qtarget:AddTargetEntity(startOxyRunPed, { + options = { + { + name = 'startOxyRun', + icon = Target.startOxyRunIcon, + label = Target.startOxyRunLabel, + distance = 2, + action = function() + if Config.RequireItem then + local hasItem = lib.callback.await('lation_oxyrun:hasItem', false, Config.RequireItemName) + if hasItem >= Config.RequireItemAmount then + startOxyRunDialog() + else + ShowNotification(Notifications.startOxyRunPedName, Notifications.startOxyRunDidntHaveItemDescription, 'error') + end + else + startOxyRunDialog() + end + end + } + } + }) +end + +-- Deletes the mission start ped when player exits the defined area +function startOxyRun:onExit() + DeleteEntity(startOxyRunPed) + qtarget:RemoveTargetEntity(startOxyRunPed, nil) +end + +-- Function that handles the actual spawning of the ped, etc +function spawnStartOxyRunPed() + lib.RequestModel(Config.startOxyRunPedModel) + startOxyRunPed = CreatePed(0, Config.startOxyRunPedModel, Config.StartOxyRunLocation, Config.StartOxyRunPedHeading, false, true) + FreezeEntityPosition(startOxyRunPed, true) + SetBlockingOfNonTemporaryEvents(startOxyRunPed, true) + SetEntityInvincible(startOxyRunPed, true) + Entity(startOxyRunPed).state:set("sellDrugs", true, true) -- disables sell drugs option on peds +end + +-- Used for debugging +AddEventHandler('onResourceRestart', function(resource) + if resource == GetCurrentResourceName() then + missionActive = false + end end) \ No newline at end of file diff --git a/config.lua b/config.lua index 2ef6755..ad395e6 100644 --- a/config.lua +++ b/config.lua @@ -46,21 +46,31 @@ Config.AvailableDoctorListLocation = vec3(343.1629, -1399.8206, 32.5092) -- The Config.EnableEffects = { -- When using an oxycontin, do you want effects enabled? enable = true, -- If so, make sure this is true. If you don't want any effects, set this to false health = { - enable = true, - amount = 200 + enable = true, -- Do you want to apply health to the player when this drug is used? (True if yes, false if no) + amount = 200 -- If enable = true, how much health do you want to apply? (200 is full health) }, armor = { - enable = false, - amount = 100 + enable = true, -- Do you want to apply armor to the player when this drug is used? (True if yes, false if no) + amount = 100 -- If enable = true, how much armor do you want to apply? (100 is full armor) + }, + speed = { + enable = true, -- Do you want to apply a speed "boost" to the player when this drug is used? (True if yes, false if no) + multiplier = 1.49, -- 1.49 is the maximum speed multiplier that works on FiveM, anything over 1.49 will not work and be set to normal speed + duration = 10 -- How long in seconds the speed multiplier should work for + }, + screenEffect = { + enable = true, -- Do you want to apply a screen effect when this drug is used? (True if yes, false if no) + effect = 'glasses_pink', -- If enableScreenEffect is true, what effect do you want? (Huge list of options: https://forum.cfx.re/t/timecyclemodifier-index-and-name-list/1419389) + duration = 10000 -- If enable is true, how long in seconds do you want the effect to last? } } --[[ Webhook Configs ]] -Config.EnableWebhook = false -- If you want Discord webhook logs, enable this with true. If not, disable it with false. +Config.EnableWebhook = true -- If you want Discord webhook logs, enable this with true. If not, disable it with false. Config.WebhookLink = '' -- The webhook link for logs -Config.WebhookName = 'ServerName' -- The Discord bot name for the webhook -Config.WebhookAvatarIcon = 'https://www.elyrsps.net/images/shared/Lation/15.01.09-10.06.23.png' -- The webhook avatar image -Config.WebhookFooterIcon = 'https://www.elyrsps.net/images/shared/Lation/15.01.09-10.06.23.png' -- The webhook footer image +Config.WebhookName = 'Oxy' -- The Discord bot name for the webhook +Config.WebhookAvatarIcon = '' -- The webhook avatar image +Config.WebhookFooterIcon = '' -- The webhook footer image --[[ String Configs ]] Notifications = { @@ -70,6 +80,7 @@ Notifications = { pharmacyDescription = 'You cancelled filling the script', pharmacyItemNotFound = 'There is nothing here for you - try again later', startOxyRunPedName = 'Aaron', + notEnoughMoney = 'Quit wasting my time bro, come back when you ain\'t broke', startOxyRunCancelDescription = 'Come back whenever you are ready', startOxyRunPart2CancelDescription = 'Quit messin\' with me man, I don\'t have time for this..', startOxyRunDidntHaveItemDescription = 'I got nothing for you man, leave me alone.', diff --git a/fxmanifest.lua b/fxmanifest.lua index 2c4faba..3aa9b96 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -1,4 +1,4 @@ --- Discord: https://discord.gg/9EbY4nM5uu +-- For support, scripts & more join our Discord here: https://discord.gg/9EbY4nM5uu fx_version 'cerulean' game 'gta5' @@ -6,18 +6,19 @@ lua54 'yes' author 'iamlation' description 'A unique take on oxy runs for FiveM' -version '1.0.0' +version '1.1.0' client_scripts { + 'bridge/client.lua', 'client/*.lua', } server_scripts { + 'bridge/server.lua', 'server/*.lua', } shared_scripts { 'config.lua', - '@es_extended/imports.lua', '@ox_lib/init.lua' } \ No newline at end of file diff --git a/server/server.lua b/server/server.lua index 35d4834..d1c689e 100644 --- a/server/server.lua +++ b/server/server.lua @@ -4,7 +4,6 @@ local discordName = Config.WebhookName local discordImage = Config.WebhookAvatarIcon local pickADoctor = math.random(1, #Config.DoctorNames) local selectADoctor = Config.DoctorNames[pickADoctor] -local getInputData -- Event for Discord webhook logs function sendToDiscord(name, message, color, timestamp) @@ -36,81 +35,96 @@ end) -- Event that officially starts the mission lib.callback.register('lation_oxyrun:startOxyRun', function(source, price) local source = source - local ped = ESX.GetPlayerFromId(source) - ox_inventory:AddItem(ped.source, Config.BlankPrescription, Config.BlankPrescriptionRewardAmount) - ox_inventory:RemoveItem(ped.source, Config.Money, price) - if Config.EnableWebhook then - sendToDiscord('Blank Prescription', '**'..ped.getName().. '** (*ID: ' ..ped.source.. '*) has purchased a blank prescription for $' ..price.. ' \n **Identifier:** ||*' ..ped.getIdentifier()..'*||', 16776960) - end -end) - --- Makes the blank_prescription item usable, prompts player to input the information then applies the metadata -ESX.RegisterUsableItem('blank_prescription', function(source) - local source = source - local ped = ESX.GetPlayerFromId(source) - getInputData = lib.callback.await('lation_oxyrun:fillPrescriptionInfo', ped.source) - if getInputData == nil then return end - local blankPrescription = ox_inventory:Search(ped.source, 1, Config.BlankPrescription) - for k, v in pairs(blankPrescription) do - blankPrescription = v - break + local player = GetPlayer(source) + local playerName = GetName(source) + local playerIdentifier = GetIdentifier(source) + local playerBalance = GetPlayerAccountFunds(source, Config.Money) + local playerId = player.source or player.PlayerData.source + if playerBalance < price then + return false + else + ox_inventory:AddItem(playerId, Config.BlankPrescription, Config.BlankPrescriptionRewardAmount) + ox_inventory:RemoveItem(playerId, Config.Money, price) + if Config.EnableWebhook then + sendToDiscord('Blank Prescription', '**'..playerName.. '** (*ID: ' ..playerId.. '*) has purchased a blank prescription for $' ..price.. ' \n **Identifier:** ||*' ..playerIdentifier..'*||', 16776960) end - ox_inventory:RemoveItem(ped.source, blankPrescription.name, 1) - blankPrescription.metadata.image = 'signed_prescription' - blankPrescription.metadata.label = 'Prescription' - blankPrescription.metadata.description = 'Patient Name: ' ..getInputData[1].. ' \n Patient Address: ' ..getInputData[2].. ' \n Acute Pain: ' ..tostring(getInputData[4]).. ' \n Approving Doctor: ' ..getInputData[6] - ox_inventory:AddItem(ped.source, Config.SignedPerscription, 1, blankPrescription.metadata) + return true + end end) -- Checks the players manually filled in script with the necessary information and proceeds accordingly -lib.callback.register('lation_oxyrun:getItemMetadata', function(source, item) +RegisterNetEvent('lation_oxyrun:getItemMetadata') +AddEventHandler('lation_oxyrun:getItemMetadata', function() local source = source - local ped = ESX.GetPlayerFromId(source) - local playerName = ped.getName() - if string.lower(playerName) ~= string.lower(getInputData[1]) or string.lower(selectADoctor) ~= string.lower(getInputData[6]) or getInputData[4] ~= true then - lib.callback.await('lation_oxyrun:fakeScript', source) - ox_inventory:RemoveItem(ped.source, Config.SignedPerscription, 1) + local player = GetPlayer(source) + local playerName = GetName(source) + local playerIdentifier = GetIdentifier(source) + local playerId = player.source or player.PlayerData.source + local prescriptionData = lib.callback.await('lation_oxyrun:getPrescriptionData', playerId) + if string.lower(playerName) ~= string.lower(prescriptionData[1]) or string.lower(selectADoctor) ~= string.lower(prescriptionData[6]) or prescriptionData[4] ~= true then + TriggerClientEvent('lation_oxyrun:fakeScript', playerId) + ox_inventory:RemoveItem(playerId, Config.SignedPerscription, 1) if Config.EnableWebhook then - sendToDiscord('Fraudulent Prescription', '**' ..playerName.. '** (*ID: ' ..ped.source.. '*) has attempted to get a prescription filled but was found to be fraudulent. \n **Identifier:** ||*' ..ped.getIdentifier()..'*||', 15548997) + sendToDiscord('Fraudulent Prescription', '**' ..playerName.. '** (*ID: ' ..playerId.. '*) has attempted to get a prescription filled but was found to be fraudulent. \n **Identifier:** ||*' ..playerIdentifier..'*||', 15548997) end else - ox_inventory:RemoveItem(ped.source, Config.SignedPerscription, 1) - ox_inventory:AddItem(ped.source, Config.OxyBottleItem, Config.OxyBottleQuantity) + ox_inventory:RemoveItem(playerId, Config.SignedPerscription, 1) + ox_inventory:AddItem(playerId, Config.OxyBottleItem, Config.OxyBottleQuantity) if Config.EnableWebhook then - sendToDiscord('Successful Prescription', '**' ..playerName.. '** (*ID: ' ..ped.source.. '*) has successfully filled their prescription. \n **Identifier:** ||*' ..ped.getIdentifier()..'*||', 65280) + sendToDiscord('Successful Prescription', '**' ..playerName.. '** (*ID: ' ..playerId.. '*) has successfully filled their prescription. \n **Identifier:** ||*' ..playerIdentifier..'*||', 65280) end end end) --- Makes the oxy_bottle item usable and rewards you with individual oxycontin -ESX.RegisterUsableItem('oxy_bottle', function(source) +RegisterUsableItem(Config.BlankPrescription, function(source) local source = source - local ped = ESX.GetPlayerFromId(source) - local openedBottle = lib.callback.await('lation_oxyrun:openOxyBottle', source, result) - if openedBottle then - ox_inventory:RemoveItem(ped.source, Config.OxyBottleItem, 1) - ox_inventory:AddItem(ped.source, Config.OxyPillItem, Config.OxyPillQuantity) - else - TriggerClientEvent('ox_lib:notify', ped.source, { title = Notifications.oxyBottleTitle, description = Notifications.oxyBottleDescription, type = 'error', icon = Notifications.icon, position = Notifications.position }) + local player = GetPlayer(source) + local playerId = player.source or player.PlayerData.source + local getInputData = lib.callback.await('lation_oxyrun:fillPrescriptionInfo', playerId) + if getInputData == nil then return end + local blankPrescription = ox_inventory:Search(playerId, 1, Config.BlankPrescription) + for _, v in pairs(blankPrescription) do + blankPrescription = v + break + end + ox_inventory:RemoveItem(playerId, blankPrescription.name, 1) + blankPrescription.metadata.image = 'signed_prescription' + blankPrescription.metadata.label = 'Prescription' + blankPrescription.metadata.description = 'Patient Name: ' ..getInputData[1].. ' \n Patient Address: ' ..getInputData[2].. ' \n Acute Pain: ' ..tostring(getInputData[4]).. ' \n Approving Doctor: ' ..getInputData[6] + ox_inventory:AddItem(playerId, Config.SignedPerscription, 1, blankPrescription.metadata) +end) + +RegisterUsableItem(Config.OxyBottleItem, function(source) + local source = source + local player = GetPlayer(source) + local playerId = player.source or player.PlayerData.source + local openedBottle = lib.callback.await('lation_oxyrun:openOxyBottle', playerId) + if openedBottle then + ox_inventory:RemoveItem(playerId, Config.OxyBottleItem, 1) + ox_inventory:AddItem(playerId, Config.OxyPillItem, Config.OxyPillQuantity) end end) --- Makes the oxycontin itself usable, for features such as health, etc -ESX.RegisterUsableItem('oxycontin', function(source) +RegisterUsableItem(Config.OxyPillItem, function(source) local source = source - local ped = ESX.GetPlayerFromId(source) - local usedOxycontin = lib.callback.await('lation_oxyrun:useOxycontin', source, result) + local player = GetPlayer(source) + local playerId = player.source or player.PlayerData.source + local usedOxycontin = lib.callback.await('lation_oxyrun:useOxycontin', playerId) if usedOxycontin then - ox_inventory:RemoveItem(ped.source, Config.OxyPillItem, 1) + ox_inventory:RemoveItem(playerId, Config.OxyPillItem, 1) if Config.EnableEffects.enable then if Config.EnableEffects.health.enable then - lib.callback.await('lation_oxyrun:setPedHealth', source) - if Config.EnableEffects.armor.enable then - lib.callback.await('lation_oxyrun:setPedArmor', source) - end + TriggerClientEvent('lation_oxyrun:setPedHealth', playerId) + end + if Config.EnableEffects.armor.enable then + TriggerClientEvent('lation_oxyrun:setPedArmor', playerId) + end + if Config.EnableEffects.speed.enable then + TriggerClientEvent('lation_oxyrun:applySpeed', playerId) + end + if Config.EnableEffects.screenEffect.enable then + TriggerClientEvent('lation_oxyrun:applyScreenEffect', playerId) end end - else - TriggerClientEvent('ox_lib:notify', ped.source, { title = Notifications.oxycontinTitle, description = Notifications.oxycontinDescription, type = 'error', icon = Notifications.icon, position = Notifications.position }) end end) \ No newline at end of file