├── README.md ├── bridge ├── client │ ├── esx.lua │ ├── nd.lua │ ├── ox.lua │ └── qb.lua └── server │ ├── esx.lua │ ├── nd.lua │ ├── ox.lua │ └── qb.lua ├── client ├── cl_main.lua └── utils.lua ├── configs ├── client.lua └── server.lua ├── fxmanifest.lua └── server ├── sv_main.lua └── utils.lua /README.md: -------------------------------------------------------------------------------- 1 |
2 |

xt-meterrobbery

3 |
4 |
5 | 6 | # Features: 7 | - Rob parking meters 8 | - Server synced cooldowns for all parking meters 9 | - Police notifications 10 | - Server distance checks 11 | - Particle effects & animations 12 | - QB / QBX / ESX / OX / ND Bridge Support 13 | 14 | # Dependencies: 15 | - [ox_lib](https://github.com/overextended/ox_lib/releases) 16 | - [ox_inventory](https://github.com/overextended/ox_inventory/releases) 17 | - [ox_target](https://github.com/overextended/ox_target/releases) 18 | -------------------------------------------------------------------------------- /bridge/client/esx.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') ~= 'started' then return end 2 | 3 | AddEventHandler('esx:playerLoaded', function() 4 | TriggerEvent('xt-meterrobbery:client:onLoad') 5 | end) 6 | 7 | AddEventHandler('esx:onPlayerLogout', function() 8 | TriggerEvent('xt-meterrobbery:client:onUnload') 9 | end) -------------------------------------------------------------------------------- /bridge/client/nd.lua: -------------------------------------------------------------------------------- 1 | if not lib.checkDependency('ND_Core', '2.0.0') then return end 2 | 3 | AddEventHandler('ND:characterLoaded', function() 4 | TriggerEvent('xt-meterrobbery:client:onLoad') 5 | end) 6 | 7 | AddEventHandler('ND:characterUnloaded', function() 8 | TriggerEvent('xt-meterrobbery:client:onUnload') 9 | end) -------------------------------------------------------------------------------- /bridge/client/ox.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('ox_core') ~= 'started' then return end 2 | 3 | local file = ('imports/%s.lua'):format(IsDuplicityVersion() and 'server' or 'client') 4 | local import = LoadResourceFile('ox_core', file) 5 | local chunk = assert(load(import, ('@@ox_core/%s'):format(file))) 6 | chunk() 7 | 8 | AddEventHandler('ox:playerLoaded', function() 9 | TriggerEvent('xt-meterrobbery:client:onLoad') 10 | end) 11 | 12 | AddEventHandler('ox:playerLogout', function() 13 | TriggerEvent('xt-meterrobbery:client:onUnload') 14 | end) -------------------------------------------------------------------------------- /bridge/client/qb.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-core') ~= 'started' then return end 2 | 3 | AddEventHandler('QBCore:Client:OnPlayerLoaded', function() 4 | TriggerEvent('xt-meterrobbery:client:onLoad') 5 | end) 6 | 7 | AddEventHandler('QBCore:Client:OnPlayerUnload', function() 8 | TriggerEvent('xt-meterrobbery:client:onUnload') 9 | end) -------------------------------------------------------------------------------- /bridge/server/esx.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') ~= 'started' then return end 2 | 3 | local ESX = exports['es_extended']:getSharedObject() 4 | 5 | local convertMoney = { 6 | ["cash"] = "money", 7 | ["bank"] = "bank" 8 | } 9 | 10 | function getPlayer(src) 11 | return ESX.GetPlayerFromId(src) 12 | end 13 | 14 | function getPlayerJob(src) 15 | local Player = getPlayer(src) 16 | return Player.job.name, Player.job.grade 17 | end 18 | 19 | function addMoney(src, amount, mType, reason) 20 | local type = convertMoney[mType] 21 | if not type then return end 22 | 23 | local player = getPlayer(src) 24 | if not player then return end 25 | 26 | player.addAccountMoney(type, amount, reason) 27 | 28 | return true 29 | end -------------------------------------------------------------------------------- /bridge/server/nd.lua: -------------------------------------------------------------------------------- 1 | if not lib.checkDependency('ND_Core', '2.0.0') then return end 2 | 3 | NDCore = {} 4 | 5 | lib.load('@ND_Core.init') 6 | 7 | function getPlayer(src) 8 | return NDCore.getPlayer(src) 9 | end 10 | 11 | function getPlayerJob(src) 12 | local Player = getPlayer(src) 13 | return Player.getJob() 14 | end 15 | 16 | function addMoney(src, amount, mType, reason) 17 | local player = getPlayer(src) 18 | if not player then return end 19 | 20 | return player.addMoney(mType, amount, reason or "unknown") 21 | end -------------------------------------------------------------------------------- /bridge/server/ox.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('ox_core') ~= 'started' then return end 2 | 3 | local file = ('imports/%s.lua'):format(IsDuplicityVersion() and 'server' or 'client') 4 | local import = LoadResourceFile('ox_core', file) 5 | local chunk = assert(load(import, ('@@ox_core/%s'):format(file))) 6 | chunk() 7 | 8 | function getPlayer(id) 9 | return Ox.GetPlayer(id) 10 | end 11 | 12 | function getPlayerJob(src) 13 | local player = getPlayer(src) 14 | return player.getGroup() 15 | end 16 | 17 | function addMoney(src, amount, mType, reason) 18 | if mType == 'cash' then 19 | return exports.ox_inventory:AddItem(src, 'money', amount) 20 | else 21 | return 22 | end 23 | end -------------------------------------------------------------------------------- /bridge/server/qb.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-core') ~= 'started' then return end 2 | 3 | local QBCore = exports['qb-core']:GetCoreObject() 4 | 5 | function getPlayer(src) 6 | return QBCore.Functions.GetPlayer(src) 7 | end 8 | 9 | function getPlayerJob(src) 10 | local player = getPlayer(src) 11 | return player.PlayerData.job.name, player.PlayerData.job.grade.level 12 | end 13 | 14 | function addMoney(src, amount, mType, reason) 15 | local player = getPlayer(src) 16 | if not player then return end 17 | return player.Functions.AddMoney(mType, amount, reason or "unknown") 18 | end -------------------------------------------------------------------------------- /client/cl_main.lua: -------------------------------------------------------------------------------- 1 | local clConfig = require 'configs.client' 2 | 3 | -- Rob Meter -- 4 | function robParkingMeter(data) 5 | if not data.entity then return end 6 | 7 | local playerPed = cache.ped 8 | local playerPedPos = GetEntityCoords(playerPed, true) 9 | 10 | local copCount = lib.callback.await('xt-meterrobbery:server:getPoliceCount', false) 11 | if copCount < clConfig.MinimumPolice then 12 | lib.notify({ title = 'Not enough Police!', type = 'error' }) 13 | return 14 | end 15 | 16 | local onCooldown = lib.callback.await('xt-meterrobbery:server:getMeterCooldown', false, data.entity) 17 | if onCooldown then 18 | lib.notify({ title = 'This meter is broken!', type = 'error' }) 19 | return 20 | end 21 | 22 | TriggerEvent('xt-meterrobbery:client:AlertPolice') 23 | 24 | lib.requestAnimDict('anim@melee@machete@streamed_core@') 25 | TaskPlayAnim(cache.ped, 'anim@melee@machete@streamed_core@', 'plyr_walking_attack_a', 3.0, 3.0, -1, 16, 0, false, false, false) 26 | RemoveAnimDict('anim@melee@machete@streamed_core@') 27 | 28 | local success = clConfig.MinigameFunction() 29 | if not success then 30 | ClearPedTasks(cache.ped) 31 | return 32 | end 33 | 34 | if lib.progressCircle({ 35 | label = 'Opening Parking Meter...', 36 | duration = (clConfig.OpenMeterLength * 1000), 37 | position = 'bottom', 38 | useWhileDead = false, 39 | canCancel = true, 40 | disable = { car = true, move = true }, 41 | anim = { dict = 'anim@melee@machete@streamed_core@', clip = 'plyr_walking_attack_a' }, 42 | }) then 43 | 44 | playPTFX(data.entity) 45 | 46 | if lib.progressCircle({ 47 | label = 'Robbing Parking Meter...', 48 | duration = (clConfig.RobbingLength * 1000), 49 | position = 'bottom', 50 | useWhileDead = false, 51 | canCancel = true, 52 | disable = { car = true, move = true }, 53 | anim = { dict = 'anim@scripted@player@freemode@tun_prep_grab_midd_ig3@male@', clip = 'tun_prep_grab_midd_ig3' }, 54 | }) then 55 | ClearPedTasks(cache.ped) 56 | local meterInfo = { coords = data.coords, entity = data.entity } 57 | local getMoney = lib.callback.await('xt-meterrobbery:server:robParkingMeter', false, meterInfo) 58 | if getMoney then return end 59 | else 60 | ClearPedTasks(cache.ped) 61 | lib.notify({ title = 'Canceled...', type = 'error' }) 62 | end 63 | else 64 | ClearPedTasks(cache.ped) 65 | lib.notify({ title = 'Canceled...', type = 'error' }) 66 | end 67 | end 68 | 69 | -- Police Alert -- 70 | RegisterNetEvent('xt-meterrobbery:client:AlertPolice', function() 71 | local pCoords = GetEntityCoords(cache.ped, true) 72 | local chance = math.random(1, 100) 73 | if chance <= clConfig.PoliceChance then 74 | clConfig.DispatchFunction(pCoords) 75 | end 76 | end) 77 | 78 | -- Handlers -- 79 | AddEventHandler('onResourceStart', function(resource) 80 | if resource ~= GetCurrentResourceName() then return end 81 | createMeterTargets() 82 | end) 83 | 84 | AddEventHandler('onResourceStop', function(resource) 85 | if resource ~= GetCurrentResourceName() then return end 86 | removeMeterTargets() 87 | end) 88 | 89 | RegisterNetEvent('xt-meterrobbery:client:onLoad', function() 90 | createMeterTargets() 91 | end) 92 | 93 | RegisterNetEvent('xt-meterrobbery:client:onUnload', function() 94 | removeMeterTargets() 95 | end) -------------------------------------------------------------------------------- /client/utils.lua: -------------------------------------------------------------------------------- 1 | local clConfig = require 'configs.client' 2 | 3 | function createMeterTargets() 4 | exports.ox_target:addModel(clConfig.Models, { 5 | { 6 | name = 'robMeters', 7 | label = 'Rob Parking Meter', 8 | icon = 'fas fa-coins', 9 | distance = 2.0, 10 | items = clConfig.AcceptedItems, 11 | anyItem = true, 12 | onSelect = function(data) 13 | robParkingMeter(data) 14 | end, 15 | } 16 | }) 17 | end 18 | 19 | function removeMeterTargets() 20 | exports.ox_target:removeModel(clConfig.Models, 'robMeters') 21 | end 22 | 23 | function playPTFX(ENT) 24 | local entCoords = GetEntityCoords(ENT) 25 | local offset, rotation = vec3(entCoords.x, entCoords.y, entCoords.z), GetEntityRotation(ENT) 26 | local dict, ptfx = 'core', 'ent_brk_coins' 27 | 28 | lib.requestNamedPtfxAsset(dict, 1000) 29 | 30 | UseParticleFxAsset(dict) 31 | 32 | local startPTFX = StartParticleFxLoopedAtCoord(ptfx, offset.x, offset.y, (offset.z + 1), rotation.x, rotation.y, rotation.z, 1.0, false, false, false) 33 | 34 | RemoveNamedPtfxAsset(dict) 35 | StopParticleFxLooped(startPTFX, false) 36 | end -------------------------------------------------------------------------------- /configs/client.lua: -------------------------------------------------------------------------------- 1 | return { 2 | -- Police Configs -- 3 | MinimumPolice = 0, -- Minimum police required to start 4 | PoliceChance = 50, -- Chance police are called 5 | 6 | -- Progressbar Time Configs -- 7 | OpenMeterLength = 3, 8 | RobbingLength = 2, 9 | 10 | -- Accepted Items -- 11 | -- Table of items that can be used to rob the meters 12 | -- Player only needs 1 of any of these items 13 | AcceptedItems = { 14 | 'WEAPON_CROWBAR', 15 | 'screwdriverset' 16 | }, 17 | 18 | -- Parking Meter Models -- 19 | Models = { 20 | "prop_parknmeter_01", 21 | "prop_parknmeter_02" 22 | }, 23 | 24 | -- Dispatch -- 25 | DispatchFunction = function(coords) 26 | local policeJobs = { 'police', 'lspd' } 27 | 28 | -- Add your own dispatch export/events 29 | 30 | exports["ps-dispatch"]:CustomAlert({ 31 | coords = coords, 32 | job = policeJobs, 33 | message = 'Parking Meter Robbery', 34 | dispatchCode = '10-??', 35 | firstStreet = coords, 36 | description = 'Parking Meter Robbery', 37 | radius = 0, 38 | sprite = 58, 39 | color = 1, 40 | scale = 1.0, 41 | length = 3, 42 | }) 43 | end, 44 | 45 | -- Add Your Own Minigame -- 46 | MinigameFunction = function() 47 | local keys = { '1', '2', '3', '4' } 48 | local difficulty = { 49 | {'easy', 'easy'}, 50 | {'easy', 'easy', 'easy'}, 51 | {'easy', 'easy', 'easy', 'easy'}, 52 | } 53 | local setDifficulty = difficulty[math.random(1, #difficulty)] 54 | 55 | return lib.skillCheck(setDifficulty, keys) 56 | end 57 | } -------------------------------------------------------------------------------- /configs/server.lua: -------------------------------------------------------------------------------- 1 | return { 2 | PoliceJobs = { 'police', 'lspd' }, 3 | MeterCooldowns = 20, -- Length of time meters are on cooldown (Minutes) 4 | Payout = { -- Min/Max payout in cash 5 | min = 100, 6 | max = 200 7 | } 8 | } -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | use_experimental_fxv2_oal 'yes' 3 | lua54 'yes' 4 | game 'gta5' 5 | 6 | description 'Parking Meter Robbing | xT Development' 7 | author 'xT Development' 8 | 9 | shared_scripts { '@ox_lib/init.lua' } 10 | client_scripts { 'bridge/client/*.lua', 'configs/client.lua', 'client/*.lua' } 11 | server_scripts { 'bridge/server/*.lua', 'configs/server.lua', 'server/*.lua' } -------------------------------------------------------------------------------- /server/sv_main.lua: -------------------------------------------------------------------------------- 1 | local svConfig = require 'configs.server' 2 | local activeCooldowns = {} 3 | local meterTimers = {} 4 | 5 | local function setMeterCooldown(state, entity) 6 | if activeCooldowns[entity] or activeCooldowns[entity] == state then return end 7 | 8 | activeCooldowns[entity] = state 9 | 10 | if state and not meterTimers[entity] then 11 | meterTimers[entity] = lib.timer((svConfig.MeterCooldowns * 1000), function() 12 | activeCooldowns[entity] = nil 13 | meterTimers[entity] = nil 14 | end, true) 15 | end 16 | 17 | return (activeCooldowns[entity] == state) 18 | end 19 | 20 | -- Get Meter Cooldown -- 21 | lib.callback.register('xt-meterrobbery:server:getMeterCooldown', function(source, entityID) 22 | return activeCooldowns[entityID] 23 | end) 24 | 25 | -- Return Police Count -- 26 | lib.callback.register('xt-meterrobbery:server:getPoliceCount', function(source) 27 | return getPoliceCount() 28 | end) 29 | 30 | -- Rob Meters -- 31 | lib.callback.register('xt-meterrobbery:server:robParkingMeter', function(source, meterInfo) 32 | local src = source 33 | local dist = distanceCheck(src, meterInfo.coords) 34 | local callback = false 35 | if not dist then return callback end 36 | 37 | local payOut = math.random(svConfig.Payout.min, svConfig.Payout.max) 38 | local setCoodlown = setMeterCooldown(true, meterInfo.entity) 39 | if setCoodlown then 40 | if addMoney(src, payOut, 'cash') then 41 | callback = true 42 | end 43 | end 44 | 45 | return callback 46 | end) -------------------------------------------------------------------------------- /server/utils.lua: -------------------------------------------------------------------------------- 1 | local svConfig = require 'configs.server' 2 | 3 | -- Distance Between Player & Meter -- 4 | function distanceCheck(src, meterCoords) 5 | local callback = false 6 | local pCoords = GetEntityCoords(GetPlayerPed(src)) 7 | local dist = #(meterCoords - pCoords) 8 | 9 | if dist <= 5 then 10 | callback = true 11 | end 12 | 13 | return callback 14 | end 15 | 16 | -- Check if Player has Police Job -- 17 | function hasPoliceJob(source, jobs) 18 | local callback = false 19 | local jobType = type(jobs) 20 | local playerJob = getPlayerJob(source) 21 | if jobType == 'table' then 22 | for x = 1, #jobs do 23 | if playerJob == jobs[x] then 24 | callback = true 25 | break 26 | end 27 | end 28 | else 29 | callback = (playerJob == jobs) 30 | end 31 | return callback 32 | end 33 | 34 | -- Gets Total Police -- 35 | function getPoliceCount() 36 | local allPlayers = GetPlayers() 37 | local copCount = 0 38 | 39 | for _, src in pairs(allPlayers) do 40 | if hasPoliceJob(tonumber(src), svConfig.PoliceJobs) then 41 | copCount += 1 42 | end 43 | end 44 | 45 | return copCount 46 | end --------------------------------------------------------------------------------