├── LICENSE ├── README.md ├── client └── main.lua ├── config ├── framework_sv.lua └── main.lua ├── fxmanifest.lua └── server └── main.lua /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Stamatis Petris 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 👤 Thief Script for [FiveM](https://fivem.net/) [![GitHub stars](https://img.shields.io/github/stars/PetrisGR/FiveM-Thief.svg)](https://github.com/PetrisGR/FiveM-Thief/stargazers) [![GitHub license](https://img.shields.io/github/license/PetrisGR/FiveM-Thief.svg)](https://github.com/PetrisGR/FiveM-Thief/blob/master/LICENSE) 2 | 3 | --- 4 | 5 | ![presentation-image](https://github.com/PetrisGR/FiveM-Thief/assets/121623120/6eedcb68-7e2c-4404-9b3d-13796eec2bd5) 6 | 7 | --- 8 | 9 | ## Description 10 | 11 | The "FiveM-Thief" repository, is a script designed for the multiplayer modification framework known as FiveM in Grand Theft Auto V (GTA V). This script enables players to engage in exciting gameplay where they can steal items from each other. It offers a collection of impressive animations and includes a robust anti-exploit system to ensure fair and secure gameplay. With its focus on interactive theft mechanics and attention to preventing exploits, "FiveM-Thief" provides an immersive and safe experience for players within the FiveM environment. 12 | 13 | ## Features 14 | 15 | - Engage in thrilling thief gameplay within FiveM. 16 | - Steal items from other players with immersive animations. 17 | - Robust anti-exploit system for a secure and fair experience. 18 | 19 | ## Usage 20 | 21 | 1. Make sure you have the latest [OX Library](https://github.com/overextended/ox_lib/releases/latest/download/ox_lib.zip) installed in your server resources. 22 | 2. Download the [latest version](https://github.com/PetrisGR/FiveM-Thief/releases/latest/download/petris-thief.zip). 23 | 3. Drag & Drop the zip file into your `resources` folder. 24 | 4. Un-zip the folder of the script. 25 | 5. Start the script via your server.cfg by typing `ensure petris-thief` in a new code line. 26 | 6. Launch your FiveM server. 27 | 7. Join the server and experience the thief gameplay mechanics. 28 | 8. Enjoy stealing items from other players with captivating animations. 29 | 30 | ## Contributing 31 | 32 | Contributions are welcome! Please submit a pull request if you have any ideas, suggestions, or improvements. For significant changes, please open an issue to discuss the proposed changes. 33 | 34 | ## Support 35 | 36 | If you encounter any issues or have any questions or suggestions, please feel free to [open an issue](https://github.com/PetrisGR/FiveM-Thief/issues). 37 | -------------------------------------------------------------------------------- /client/main.lua: -------------------------------------------------------------------------------- 1 | local TargetPed = nil 2 | 3 | local InputCooldowns = { 4 | ["Thief"] = 0, 5 | ["HandsUp"] = 0 6 | } 7 | 8 | local PlayerState = { 9 | ["IsStealing"] = false, 10 | ["IsBeingRobbed"] = false, 11 | ["HasHandsUp"] = false 12 | } 13 | 14 | local function StopHandsUpState() 15 | local ped = PlayerPedId() 16 | ClearPedTasks(ped) 17 | PlayerState["HasHandsUp"] = false 18 | TriggerServerEvent('Thief:Server:SetState', false) 19 | end 20 | 21 | local function LoadAnimDict(animDict) 22 | if not HasAnimDictLoaded(animDict) then 23 | RequestAnimDict(animDict) 24 | 25 | while not HasAnimDictLoaded(animDict) do 26 | Wait(0) 27 | end 28 | end 29 | end 30 | 31 | local function MakeEntityFaceEntity(entity1, entity2) 32 | local p1 = GetEntityCoords(entity1, true) 33 | local p2 = GetEntityCoords(entity2, true) 34 | 35 | local dx = p2.x - p1.x 36 | local dy = p2.y - p1.y 37 | 38 | local heading = GetHeadingFromVector_2d(dx, dy) 39 | SetEntityHeading(entity1, heading) 40 | end 41 | 42 | CreateThread(function() 43 | while true do 44 | Wait(100) 45 | if NetworkIsPlayerActive(PlayerId()) then 46 | TriggerServerEvent('Thief:Server:RegisterPlayer') 47 | break 48 | end 49 | end 50 | LoadAnimDict('anim@mugging@victim@toss_ped@') 51 | end) 52 | 53 | exports("IsPlayerStealing", function() 54 | return PlayerState["IsStealing"] 55 | end) 56 | 57 | exports("IsPlayerBeingRobbed", function() 58 | return PlayerState["IsBeingRobbed"] 59 | end) 60 | 61 | exports("HasPlayerHandsUp", function() 62 | return PlayerState["HasHandsUp"] 63 | end) 64 | 65 | RegisterNetEvent('Thief:Client:ResetThiefState') 66 | AddEventHandler('Thief:Client:ResetThiefState', function() 67 | PlayerState["IsStealing"] = false 68 | TargetPed = nil 69 | ClearPedTasks(PlayerPedId()) 70 | lib.hideContext(false) 71 | lib.closeInputDialog() 72 | end) 73 | 74 | RegisterNetEvent('Thief:Client:SetRobberyMenu') 75 | AddEventHandler('Thief:Client:SetRobberyMenu', function(targetPedNetId, targetInventory) 76 | TargetPed = NetworkGetEntityFromNetworkId(targetPedNetId) 77 | 78 | if not PlayerState["IsStealing"] then 79 | PlayerState["IsStealing"] = true 80 | TaskAimGunAtEntity(PlayerPedId(), TargetPed, -1) 81 | end 82 | 83 | local serverId = GetPlayerServerId(PlayerId()) 84 | 85 | if lib.getOpenContextMenu() then 86 | if lib.getOpenContextMenu() == 'thief_'..serverId..'' then 87 | lib.hideContext(false) 88 | end 89 | end 90 | 91 | local TargetStealableItems = {} 92 | 93 | for k,v in pairs(targetInventory) do 94 | local itemLabel = v.label 95 | 96 | if not itemLabel then itemLabel = v.item end 97 | 98 | local itemTitle = v.amount.. "x " ..itemLabel 99 | local hasAmountSelection = true 100 | 101 | if v.type == "money" then hasAmountSelection = false itemTitle = v.amount.. ""..Config.Menu["Currency"].." " ..itemLabel end 102 | if v.type == "weapon" then hasAmountSelection = false itemTitle = itemLabel.. " ("..v.amount..")" end 103 | 104 | TargetStealableItems[#TargetStealableItems + 1] = { 105 | title = itemTitle, 106 | icon = Config.Menu["IconsPath"].."/"..v.item..".png", 107 | arrow = hasAmountSelection, 108 | onSelect = function() 109 | if v.type == "item" or v.type == "money" then 110 | local input = lib.inputDialog(Config.Menu["DialogTitle"], { 111 | {type = 'input', label = Config.Menu["InputItemTitle"], default = v.label, disabled = true}, 112 | {type = 'number', label = Config.Menu["InputAmountTitle"], description = Config.Menu["InputAmountDescription"], required = true, min = 1, max = v.amount} 113 | }) 114 | 115 | if not input then 116 | lib.registerContext({ 117 | id = 'thief_'..serverId..'', 118 | title = Config.Menu["Title"], 119 | options = TargetStealableItems, 120 | onExit = function() 121 | TriggerServerEvent('Thief:Server:StopRobbery') 122 | lib.closeInputDialog() 123 | end 124 | }) 125 | 126 | lib.showContext('thief_'..serverId..'') 127 | return 128 | end 129 | 130 | TriggerServerEvent('Thief:Server:StealItem', v.type, v.item, input[2], v.data) 131 | elseif v.type == "weapon" then 132 | TriggerServerEvent('Thief:Server:StealItem', v.type, v.item, v.amount, v.data) 133 | end 134 | end 135 | } 136 | end 137 | 138 | lib.registerContext({ 139 | id = 'thief_'..serverId..'', 140 | title = Config.Menu["Title"], 141 | options = TargetStealableItems, 142 | onExit = function() 143 | TriggerServerEvent('Thief:Server:StopRobbery') 144 | lib.closeInputDialog() 145 | end 146 | }) 147 | 148 | lib.showContext('thief_'..serverId..'') 149 | end) 150 | 151 | RegisterNetEvent('Thief:Client:ConfirmState') 152 | AddEventHandler('Thief:Client:ConfirmState', function(state) 153 | local clientState = PlayerState["HasHandsUp"] 154 | 155 | if state ~= clientState then 156 | TriggerServerEvent('Thief:Server:SetState', clientState) 157 | end 158 | end) 159 | 160 | RegisterNetEvent('Thief:Client:ChangeRobbedState') 161 | AddEventHandler('Thief:Client:ChangeRobbedState', function(isBeingRobbed, thiefPed) 162 | PlayerState["IsBeingRobbed"] = isBeingRobbed 163 | if isBeingRobbed then 164 | MakeEntityFaceEntity(PlayerPedId(), NetworkGetEntityFromNetworkId(thiefPed)) 165 | else 166 | StopHandsUpState() 167 | end 168 | Config.Functions.IsBeingRobbed(isBeingRobbed) 169 | end) 170 | 171 | RegisterNetEvent('Thief:Client:SetHandsUp') 172 | AddEventHandler('Thief:Client:SetHandsUp', function() 173 | TaskPlayAnim(PlayerPedId(), Config.Animation["Dictionary"], Config.Animation["Name"], Config.Animation["BlendInSpeed"], Config.Animation["BlendOutSpeed"], -1, 50, 0, false, false, false) 174 | PlayerState["HasHandsUp"] = true 175 | TriggerServerEvent('Thief:Server:SetState', true) 176 | end) 177 | 178 | Citizen.CreateThread(function() 179 | while true do 180 | Citizen.Wait(1000) 181 | 182 | local ped = PlayerPedId() 183 | 184 | if IsEntityDead(ped) then 185 | if PlayerState["IsStealing"] then 186 | TriggerServerEvent('Thief:Server:RobberyCancelled') 187 | lib.hideContext(false) 188 | lib.closeInputDialog() 189 | elseif PlayerState["HasHandsUp"] then 190 | StopHandsUpState() 191 | elseif PlayerState["IsBeingRobbed"] then 192 | TriggerServerEvent('Thief:Server:RobberyCancelled') 193 | else 194 | Citizen.Wait(2500) 195 | end 196 | else 197 | Citizen.Wait(1500) 198 | end 199 | end 200 | end) 201 | 202 | Citizen.CreateThread(function() 203 | while true do 204 | Citizen.Wait(500) 205 | if PlayerState["HandsUp"] then 206 | if not IsEntityPlayingAnim(PlayerPedId(), Config.Animation["Dictionary"], Config.Animation["Name"], 3) then 207 | StopHandsUpState() 208 | if PlayerState["IsBeingRobbed"] then 209 | TriggerServerEvent('Thief:Server:RobberyCancelled') 210 | end 211 | end 212 | else 213 | Citizen.Wait(1000) 214 | end 215 | end 216 | end) 217 | 218 | Citizen.CreateThread(function() 219 | while true do 220 | Citizen.Wait(500) 221 | if PlayerState["IsStealing"] then 222 | 223 | local myCoords = GetEntityCoords(PlayerPedId()) 224 | 225 | if not DoesEntityExist(TargetPed) then TriggerServerEvent('Thief:Server:RobberyCancelled') lib.hideContext(false) lib.closeInputDialog() end 226 | 227 | local targetCoords = GetEntityCoords(TargetPed) 228 | 229 | if (#(myCoords - targetCoords) > Config.Settings["MaxDistance"]) then TriggerServerEvent('Thief:Server:RobberyCancelled') lib.hideContext(false) lib.closeInputDialog() end 230 | else 231 | Citizen.Wait(1500) 232 | end 233 | end 234 | end) 235 | 236 | Citizen.CreateThread(function() 237 | while true do 238 | Citizen.Wait(0) 239 | if PlayerState["IsBeingRobbed"] then 240 | DisableAllControlActions(0) 241 | DisablePlayerFiring(PlayerId(), true) 242 | else 243 | Citizen.Wait(500) 244 | end 245 | end 246 | end) 247 | 248 | Citizen.CreateThread(function() 249 | while true do 250 | Citizen.Wait(1000) 251 | if InputCooldowns["Thief"] >= 1 then 252 | InputCooldowns["Thief"] = InputCooldowns["Thief"] - 1 253 | end 254 | if InputCooldowns["HandsUp"] >= 1 then 255 | InputCooldowns["HandsUp"] = InputCooldowns["HandsUp"] - 1 256 | end 257 | end 258 | end) 259 | 260 | RegisterCommand('$handsup', function() 261 | local ped = PlayerPedId() 262 | 263 | if InputCooldowns["HandsUp"] >= 1 then return end 264 | 265 | if IsPedInAnyVehicle(ped, true) then return end 266 | 267 | if PlayerState["IsBeingRobbed"] then return end 268 | 269 | if not PlayerState["HasHandsUp"] then 270 | 271 | if IsEntityDead(ped) then return end 272 | 273 | if IsPedRunning(ped) then return end 274 | 275 | InputCooldowns["HandsUp"] = Config.InputCooldowns["HandsUp"] 276 | 277 | SetCurrentPedWeapon(ped, GetHashKey("WEAPON_UNARMED")) 278 | 279 | LoadAnimDict(Config.Animation["Dictionary"]) 280 | TaskPlayAnim(ped, Config.Animation["Dictionary"], Config.Animation["Name"], Config.Animation["BlendInSpeed"], Config.Animation["BlendOutSpeed"], -1, 50, 0, false, false, false) 281 | PlayerState["HasHandsUp"] = true 282 | TriggerServerEvent('Thief:Server:SetState', true) 283 | else 284 | StopHandsUpState() 285 | end 286 | end, false) 287 | 288 | RegisterCommand('$thief', function() 289 | local ped = PlayerPedId() 290 | 291 | if InputCooldowns["Thief"] >= 1 then return end 292 | 293 | if IsPedInAnyVehicle(ped, true) then return end 294 | 295 | if not PlayerState["IsStealing"] then 296 | 297 | if IsEntityDead(ped) then return end 298 | 299 | InputCooldowns["Thief"] = Config.InputCooldowns["Thief"] 300 | 301 | if Config.Functions.CanPlayerSteal(ped) then 302 | TriggerServerEvent('Thief:Server:ThiefRequest') 303 | end 304 | else 305 | lib.hideContext(true) 306 | end 307 | end, false) 308 | 309 | RegisterKeyMapping('$handsup', 'Hands Up', 'keyboard', Config.Keybinds["HandsUp"]) 310 | RegisterKeyMapping('$thief', 'Thief', 'keyboard', Config.Keybinds["Thief"]) -------------------------------------------------------------------------------- /config/framework_sv.lua: -------------------------------------------------------------------------------- 1 | Framework = { 2 | Object = (GetResourceState("es_extended") == "started" and exports['es_extended']:getSharedObject()) or (GetResourceState("qb-core") == "started" and exports['qb-core']:GetCoreObject()) or nil, 3 | 4 | Functions = { 5 | IsPlayerAllowedToSteal = function(playerId) 6 | -- You can add anything here. 7 | end, 8 | 9 | CanPlayerBeStolen = function(playerId) 10 | -- Example: Do not allow players to steal staff. (You can change this function according to your needs) 11 | 12 | -- -- ESX 13 | -- if GetResourceState("es_extended") == "started" then 14 | -- local xPlayer = Framework.Object.GetPlayerFromId(playerId) 15 | 16 | -- if xPlayer.getGroup() ~= 'user' then 17 | -- return false 18 | -- end 19 | -- -- QBCore 20 | -- elseif GetResourceState("qb-core") == "started" then 21 | -- local xPlayer = Framework.Object.GetPlayer(playerId) 22 | -- local permissions = Framework.Object.Functions.GetPermission(source) 23 | -- local isStaff = false 24 | 25 | -- for group, hasPerms in pairs(permissions) do 26 | -- if hasPerms == true then 27 | -- isStaff = true 28 | -- end 29 | -- end 30 | 31 | -- if isStaff then 32 | -- return false 33 | -- end 34 | -- end 35 | 36 | return true 37 | end, 38 | 39 | GetTargetItems = function(playerId) 40 | -- OX Inventory 41 | if GetResourceState("ox_inventory") == "started" then 42 | local items = {} 43 | 44 | local inventory = exports.ox_inventory:GetInventory(playerId) 45 | local moneyAccounts = {"cash", "money", "black_money", "crypto"} 46 | 47 | for k,v in pairs(inventory.items) do 48 | local isMoney = false 49 | 50 | for _, account in pairs(moneyAccounts) do 51 | if v.name == account then 52 | if not IsItemBlacklisted(v.name, "Money") and v.count >= 1 then 53 | items[#items + 1] = {type = "money", item = v.name, label = v.label, amount = v.count} 54 | isMoney = true 55 | end 56 | end 57 | end 58 | 59 | if not isMoney then 60 | if string.sub(v.name, 1, 7) == "WEAPON_" then 61 | if not IsItemBlacklisted(v.name, "Weapons") and v.count >= 1 then 62 | items[#items + 1] = {type = "weapon", item = v.name, label = v.label, amount = v.count, data = v.info} 63 | end 64 | else 65 | if not IsItemBlacklisted(v.name, "Items") and v.count >= 1 then 66 | items[#items + 1] = {type = "item", item = v.name, label = v.label, amount = v.count, data = v.metadata} 67 | end 68 | end 69 | end 70 | end 71 | 72 | return items 73 | -- ESX 74 | elseif GetResourceState("es_extended") == "started" then 75 | local xPlayer = Framework.Object.GetPlayerFromId(playerId) 76 | local items = {} 77 | 78 | for k,v in pairs(xPlayer.getAccounts()) do 79 | if not IsItemBlacklisted(v.name, "Money") and v.money >= 1 then 80 | items[#items + 1] = {type = "money", item = v.name, label = v.label, amount = v.money} 81 | end 82 | end 83 | 84 | for k,v in ipairs(xPlayer.getLoadout()) do 85 | if not IsItemBlacklisted(v.name, "Weapons") then 86 | items[#items + 1] = {type = "weapon", item = v.name, label = v.label, amount = v.ammo, data = v.components} 87 | end 88 | end 89 | 90 | for k,v in pairs(xPlayer.getInventory()) do 91 | if not IsItemBlacklisted(v.name, "Items") and v.count >= 1 then 92 | items[#items + 1] = {type = "item", item = v.name, label = v.label, amount = v.count, data = v.weight} 93 | end 94 | end 95 | 96 | return items 97 | -- QBCore 98 | elseif GetResourceState("qb-core") == "started" then 99 | local Player = Framework.Object.Functions.GetPlayer(playerId) 100 | local items = {} 101 | 102 | for k,v in pairs(Framework.Object.Config.Money.MoneyTypes) do 103 | local money = Player.Functions.GetMoney(k) 104 | if not IsItemBlacklisted(k, "Money") and money >= 1 then 105 | items[#items + 1] = {type = "money", item = k, label = k, amount = money} 106 | end 107 | end 108 | 109 | for k,v in pairs(Player.PlayerData.items) do 110 | local itemData = Framework.Object.Shared.Items[v.name:lower()] 111 | 112 | if itemData['type'] == 'item' then 113 | if not IsItemBlacklisted(v.name, "Items") and v.amount >= 1 then 114 | items[#items + 1] = {type = "item", item = v.name, label = v.label, amount = v.amount, data = v.info} 115 | end 116 | end 117 | if itemData['type'] == 'weapon' then 118 | if not IsItemBlacklisted(v.name, "Weapons") and v.amount >=1 then 119 | items[#items + 1] = {type = "weapon", item = v.name, label = v.label, amount = v.amount, data = v.info} 120 | end 121 | end 122 | end 123 | 124 | return items 125 | end 126 | end, 127 | 128 | StealItem = function(thiefId, targetId, itemType, itemName, itemAmount, itemData) 129 | local stolen = false 130 | 131 | -- OX Inventory 132 | if GetResourceState("ox_inventory") == "started" then 133 | local stolen = false 134 | 135 | local xThief = exports.ox_inventory:GetInventory(thiefId) 136 | local xTarget = exports.ox_inventory:GetInventory(targetId) 137 | 138 | if itemType == "item" or itemType == "weapon" then 139 | local targetItem = exports.ox_inventory:GetItemCount(targetId, itemName) >= itemAmount 140 | 141 | if targetItem then 142 | if exports.ox_inventory:CanCarryItem(thiefId, itemName, itemAmount, itemData) then 143 | exports.ox_inventory:RemoveItem(targetId, itemName, itemAmount) 144 | exports.ox_inventory:AddItem(thiefId, itemName, itemAmount, itemData) 145 | 146 | stolen = true 147 | end 148 | end 149 | elseif itemType == "money" then 150 | local targetAccount = exports.ox_inventory:GetItemCount(targetId, itemName) >= itemAmount 151 | 152 | if targetAccount then 153 | exports.ox_inventory:RemoveItem(targetId, itemName, itemAmount) 154 | exports.ox_inventory:AddItem(thiefId, itemName, itemAmount) 155 | 156 | stolen = true 157 | end 158 | end 159 | 160 | return stolen 161 | -- ESX 162 | elseif GetResourceState("es_extended") == "started" then 163 | local xThief = Framework.Object.GetPlayerFromId(thiefId) 164 | local xTarget = Framework.Object.GetPlayerFromId(targetId) 165 | 166 | if itemType == "item" then 167 | local targetItem = xTarget.hasItem(itemName) 168 | 169 | if targetItem then 170 | if (targetItem.count >= itemAmount) and xThief.canCarryItem(itemName, itemAmount) then 171 | xTarget.removeInventoryItem(itemName, itemAmount) 172 | xThief.addInventoryItem(itemName, itemAmount) 173 | 174 | stolen = true 175 | end 176 | end 177 | elseif itemType == "weapon" then 178 | if xTarget.hasWeapon(itemName) then 179 | xTarget.removeWeapon(itemName, itemAmount) 180 | xThief.addWeapon(itemName, itemAmount) 181 | 182 | for _, component in pairs(itemData) do 183 | xThief.addWeaponComponent(itemName, component) 184 | end 185 | 186 | stolen = true 187 | end 188 | elseif itemType == "money" then 189 | local targetAccount = xTarget.getAccount(itemName) 190 | 191 | if targetAccount and targetAccount.money >= itemAmount then 192 | xTarget.removeAccountMoney(itemName, itemAmount) 193 | xThief.addAccountMoney(itemName, itemAmount) 194 | 195 | stolen = true 196 | end 197 | end 198 | 199 | return stolen 200 | -- QBCore 201 | elseif GetResourceState("qb-core") == "started" then 202 | local xThief = Framework.Object.Functions.GetPlayer(thiefId) 203 | local xTarget = Framework.Object.Functions.GetPlayer(targetId) 204 | 205 | if itemType == "item" then 206 | for k,v in pairs(xTarget.PlayerData.items) do 207 | if v.name == itemName then 208 | if (v.amount >= itemAmount) then 209 | xTarget.Functions.RemoveItem(itemName, itemAmount) 210 | xThief.Functions.AddItem(itemName, itemAmount, nil, itemData) 211 | 212 | stolen = true 213 | end 214 | end 215 | end 216 | elseif itemType == "weapon" then 217 | for k,v in pairs(xTarget.PlayerData.items) do 218 | if v.name == itemName then 219 | xTarget.Functions.RemoveItem(itemName, itemAmount) 220 | xThief.Functions.AddItem(itemName, itemAmount, nil, itemData) 221 | 222 | stolen = true 223 | end 224 | end 225 | elseif itemType == "money" then 226 | local targetAccount = xTarget.PlayerData.money[itemName] 227 | 228 | if targetAccount and targetAccount >= itemAmount then 229 | xTarget.Functions.RemoveMoney(itemName, itemAmount) 230 | xThief.Functions.AddMoney(itemName, itemAmount) 231 | 232 | stolen = true 233 | end 234 | end 235 | 236 | return stolen 237 | end 238 | end, 239 | 240 | ShowNotification = function(playerId, message) 241 | -- ESX 242 | if GetResourceState("es_extended") == "started" then 243 | TriggerClientEvent('esx:showNotification', playerId, text) 244 | -- QBCore 245 | elseif GetResourceState("qb-core") == "started" then 246 | TriggerClientEvent('QBCore:Notify', playerId, text) 247 | end 248 | end, 249 | 250 | GetIdentifier = function(playerId) 251 | -- ESX 252 | if GetResourceState("es_extended") == "started" then 253 | local xPlayer = Framework.Object.GetPlayerFromId(playerId) 254 | return xPlayer.identifier 255 | -- QBCore 256 | elseif GetResourceState("qb-core") == "started" then 257 | local xPlayer = Framework.Object.Functions.GetPlayer(playerId) 258 | return xPlayer.PlayerData.citizenid 259 | end 260 | end, 261 | 262 | BanPlayer = function(playerId) 263 | -- Player attempted to steal player while his ped was not robbing him. (Cheating / Triggering Events) 264 | DropPlayer(playerId, 'Stop Cheating!') 265 | end 266 | } 267 | } -------------------------------------------------------------------------------- /config/main.lua: -------------------------------------------------------------------------------- 1 | Config = { 2 | Keybinds = { -- You can select any keybinds from here: https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard/ 3 | ["Thief"] = "H", 4 | ["HandsUp"] = "X" 5 | }, 6 | 7 | InputCooldowns = { 8 | ["Thief"] = 2, -- Second(s) 9 | ["HandsUp"] = 1, -- Second(s) 10 | }, 11 | 12 | Settings = { 13 | ["Cooldown"] = { 14 | ["Enabled"] = false, 15 | ["Duration"] = 1, -- Minute(s) 16 | }, 17 | ["MaxDistance"] = 2.0, 18 | }, 19 | 20 | Blacklisted = { 21 | ["Areas"] = { 22 | -- {coords = vector3(0.0, 0.0, 0.0), range = 5.0}, 23 | }, 24 | ["Inventory"] = { 25 | ["Items"] = { 26 | "id_card", 27 | }, 28 | ["Money"] = { 29 | "bank", 30 | }, 31 | ["Weapons"] = { 32 | "WEAPON_STUNGUN", 33 | } 34 | }, 35 | }, 36 | 37 | Menu = { 38 | ["Title"] = "Target's Inventory", 39 | ["DialogTitle"] = "Choose Amount", 40 | ["InputItemTitle"] = "Item", 41 | ["InputAmountTitle"] = "Amount", 42 | ["InputAmountDescription"] = "Choose amount to steal.", 43 | ["Currency"] = "$", 44 | ["IconsPath"] = "nui://ox_inventory/web/images" 45 | }, 46 | 47 | Animation = { 48 | ["Dictionary"] = "missminuteman_1ig_2", 49 | ["Name"] = "handsup_enter", 50 | ["BlendInSpeed"] = 2.0, 51 | ["BlendOutSpeed"] = 4.0 52 | }, 53 | 54 | Notifications = { 55 | ["Steal"] = true, 56 | ["NoPlayersNearby"] = true 57 | }, 58 | 59 | Messages = { 60 | ["cooldown"] = "You can\'t rob right now. Try again later!", 61 | ["something_went_wrong"] = "Something went wrong.", 62 | ["you_stole"] = "You stole", 63 | ["thief_stole"] = "Thief stole", 64 | ["from_you"] = "from you", 65 | ["no_players_nearby"] = "No players with hands up nearby." 66 | }, 67 | 68 | Functions = { 69 | CanPlayerSteal = function(ped) 70 | local BlacklistedMelees = {"WEAPON_UNARMED", "WEAPON_KNUCKLE", "WEAPON_FLASHLIGHT"} 71 | 72 | if IsPedArmed(ped, 4) then 73 | return true 74 | end 75 | 76 | if IsPedArmed(ped, 1) then 77 | local approvedMelee = false 78 | 79 | for _, melee in pairs(BlacklistedMelees) do 80 | if GetHashKey(melee) == GetSelectedPedWeapon(ped) then 81 | approvedMelee = true 82 | break 83 | end 84 | end 85 | 86 | return approvedMelee 87 | end 88 | 89 | return false 90 | end, 91 | IsBeingRobbed = function(bool) 92 | local ped = PlayerPedId() 93 | if bool then 94 | -- If he's being robbed, disable some things that he shouldn't use. 95 | FreezeEntityPosition(ped, true) 96 | SetPedEnableWeaponBlocking(ped, true) 97 | else 98 | -- Once stopped being robbed, enable the things you disabled. 99 | FreezeEntityPosition(ped, false) 100 | SetPedEnableWeaponBlocking(ped, false) 101 | end 102 | end 103 | } 104 | } -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | game 'gta5' 3 | 4 | author 'Petris ' 5 | description 'Advanced Thief Script' 6 | version '1.1.0' 7 | 8 | lua54 'yes' 9 | 10 | shared_script '@ox_lib/init.lua' 11 | 12 | shared_script('config/main.lua') 13 | 14 | server_scripts { 15 | 'config/framework_sv.lua', 16 | 'server/main.lua' 17 | } 18 | 19 | client_script('client/main.lua') 20 | 21 | depedency 'ox_lib' -------------------------------------------------------------------------------- /server/main.lua: -------------------------------------------------------------------------------- 1 | local OnlinePlayers = {} 2 | local StealablePlayers = {} 3 | local ActiveRobberies = {} 4 | local Cooldowns = {} 5 | 6 | local function GetClosestTarget(playerId) 7 | local result = {} 8 | local playerCoords = GetEntityCoords(GetPlayerPed(playerId)) 9 | local playerBucket = GetPlayerRoutingBucket(playerId) 10 | 11 | for id, isOnline in pairs(OnlinePlayers) do 12 | if (Framework.Functions.CanPlayerBeStolen(id) and StealablePlayers[id] and isOnline and id ~= playerId) then 13 | local entity = GetPlayerPed(id) 14 | local coords = GetEntityCoords(entity) 15 | local dist = #(playerCoords - coords) 16 | local busy = false 17 | 18 | for k,v in pairs(ActiveRobberies) do 19 | if v == id then 20 | busy = true 21 | end 22 | end 23 | 24 | if not busy then 25 | if dist <= (result.dist or Config.Settings["MaxDistance"]) and (GetPlayerRoutingBucket(id) == playerBucket) then 26 | result = {id = id, ped = NetworkGetNetworkIdFromEntity(entity), coords = coords, dist = dist} 27 | end 28 | end 29 | end 30 | end 31 | 32 | return result 33 | end 34 | 35 | function IsItemBlacklisted(itemName, itemType) 36 | local isBlacklisted = false 37 | 38 | for _, blacklistedItem in pairs(Config.Blacklisted["Inventory"][itemType]) do 39 | if itemName == blacklistedItem then 40 | isBlacklisted = true 41 | break 42 | end 43 | end 44 | 45 | return isBlacklisted 46 | end 47 | 48 | RegisterServerEvent('Thief:Server:SetState') 49 | AddEventHandler('Thief:Server:SetState', function(hasHandsUp) 50 | local playerId = source 51 | 52 | if not StealablePlayers[playerId] and hasHandsUp then 53 | StealablePlayers[playerId] = true 54 | elseif StealablePlayers[playerId] and not hasHandsUp then 55 | StealablePlayers[playerId] = nil 56 | end 57 | 58 | TriggerClientEvent('Thief:Client:ConfirmState', playerId, hasHandsUp) 59 | end) 60 | 61 | RegisterServerEvent('Thief:Server:StealItem') 62 | AddEventHandler('Thief:Server:StealItem', function(itemType, itemName, itemAmount, itemData) 63 | local playerId = source 64 | 65 | if not ActiveRobberies[playerId] then Framework.Functions.BanPlayer(playerId) return end 66 | 67 | if Framework.Functions.StealItem(playerId, ActiveRobberies[playerId], itemType, itemName, itemAmount, itemData) then 68 | local targetPed = GetPlayerPed(ActiveRobberies[playerId]) 69 | TaskPlayAnim(targetPed, 'anim@mugging@victim@toss_ped@', 'throw_object_right_pocket_female', 1.0, 2.0, 4000, 50, false, false, false, false, false) 70 | Wait(4000) 71 | TriggerClientEvent('Thief:Client:SetHandsUp', ActiveRobberies[playerId]) 72 | if Config.Notifications["Steal"] then 73 | if itemType == "item" then 74 | Framework.Functions.ShowNotification(playerId, Config["Messages"]["you_stole"].. " "..itemAmount.."x "..itemName.."") 75 | Framework.Functions.ShowNotification(ActiveRobberies[playerId], Config["Messages"]["thief_stole"].. " "..itemAmount.."x "..itemName.." " ..Config["Messages"]["from_you"]) 76 | elseif itemType == "money" then 77 | Framework.Functions.ShowNotification(playerId, Config["Messages"]["you_stole"].. " "..itemAmount..""..Config.Menu["Currency"].." "..itemName.."") 78 | Framework.Functions.ShowNotification(ActiveRobberies[playerId], Config["Messages"]["thief_stole"].. " "..itemAmount..""..Config.Menu["Currency"].." "..itemName.." " ..Config["Messages"]["from_you"]) 79 | elseif itemType == "weapon" then 80 | Framework.Functions.ShowNotification(playerId, Config["Messages"]["you_stole"].. " "..itemName.."") 81 | Framework.Functions.ShowNotification(ActiveRobberies[playerId], Config["Messages"]["thief_stole"].. " "..itemName.." " ..Config["Messages"]["from_you"]) 82 | end 83 | end 84 | else 85 | Framework.Functions.ShowNotification(playerId, Config["Messages"]["something_went_wrong"]) 86 | end 87 | 88 | TriggerClientEvent('Thief:Client:SetRobberyMenu', playerId, NetworkGetNetworkIdFromEntity(GetPlayerPed(ActiveRobberies[playerId])), Framework.Functions.GetTargetItems(ActiveRobberies[playerId])) 89 | end) 90 | 91 | RegisterServerEvent('Thief:Server:ThiefRequest') 92 | AddEventHandler('Thief:Server:ThiefRequest', function() 93 | local playerId = source 94 | local ped = GetPlayerPed(playerId) 95 | 96 | if Cooldowns[Framework.Functions.GetIdentifier(playerId)] then Framework.Functions.ShowNotification(playerId, Config.Messages["cooldown"]) return end 97 | 98 | local isAreaBlacklisted = false 99 | 100 | for k,v in pairs(Config.Blacklisted["Areas"]) do 101 | if #(GetEntityCoords(GetPlayerPed(playerId)) - v.coords) < v.range then 102 | isAreaBlacklisted = true 103 | break 104 | end 105 | end 106 | 107 | if isAreaBlacklisted then return end 108 | 109 | local closestTarget = GetClosestTarget(playerId) 110 | 111 | if not DoesEntityExist(ped) then return end 112 | 113 | if not closestTarget.id then 114 | if Config.Notifications["NoPlayersNearby"] then 115 | Framework.Functions.ShowNotification(playerId, Config.Messages["no_players_nearby"]) 116 | end 117 | return 118 | end 119 | 120 | if not DoesEntityExist(NetworkGetEntityFromNetworkId(closestTarget.ped)) then 121 | if Config.Notifications["NoPlayersNearby"] then 122 | Framework.Functions.ShowNotification(playerId, Config.Messages["no_players_nearby"]) 123 | end 124 | return 125 | end 126 | 127 | ActiveRobberies[playerId] = closestTarget.id 128 | 129 | if Config.Settings["Cooldown"]["Enabled"] then 130 | Cooldowns[Framework.Functions.GetIdentifier(playerId)] = Config.Settings["Cooldown"]["Duration"] 131 | end 132 | 133 | TriggerClientEvent('Thief:Client:ChangeRobbedState', ActiveRobberies[playerId], true, NetworkGetNetworkIdFromEntity(GetPlayerPed(playerId))) 134 | TriggerClientEvent('Thief:Client:SetRobberyMenu', playerId, closestTarget.ped, Framework.Functions.GetTargetItems(ActiveRobberies[playerId])) 135 | end) 136 | 137 | RegisterServerEvent('Thief:Server:StopRobbery') 138 | AddEventHandler('Thief:Server:StopRobbery', function() 139 | local playerId = source 140 | 141 | if ActiveRobberies[playerId] then 142 | TriggerClientEvent('Thief:Client:ResetThiefState', playerId) 143 | TriggerClientEvent('Thief:Client:ChangeRobbedState', ActiveRobberies[playerId], false) 144 | ActiveRobberies[playerId] = nil 145 | end 146 | end) 147 | 148 | RegisterServerEvent('Thief:Server:RobberyCancelled') 149 | AddEventHandler('Thief:Server:RobberyCancelled', function() 150 | local playerId = source 151 | 152 | for thief,target in pairs(ActiveRobberies) do 153 | if playerId == thief or playerId == target then 154 | TriggerClientEvent('Thief:Client:ResetThiefState', thief) 155 | TriggerClientEvent('Thief:Client:ChangeRobbedState', target, false) 156 | ActiveRobberies[thief] = nil 157 | break 158 | end 159 | end 160 | end) 161 | 162 | RegisterServerEvent('Thief:Server:RegisterPlayer') 163 | AddEventHandler('Thief:Server:RegisterPlayer', function() 164 | local playerId = source 165 | OnlinePlayers[playerId] = true 166 | end) 167 | 168 | AddEventHandler('playerDropped', function(reason) 169 | local playerId = source 170 | OnlinePlayers[playerId] = nil 171 | if ActiveRobberies[playerId] then 172 | TriggerClientEvent('Thief:Client:ChangeRobbedState', ActiveRobberies[playerId], false) 173 | ActiveRobberies[playerId] = nil 174 | end 175 | end) 176 | 177 | if Config.Settings["Cooldown"]["Enabled"] then 178 | CreateThread(function() 179 | while true do 180 | Citizen.Wait(60000) 181 | for k,v in pairs(Cooldowns) do 182 | Cooldowns[k] = v - 1 183 | if Cooldowns[k] < 1 then 184 | Cooldowns[k] = nil 185 | end 186 | end 187 | end 188 | end) 189 | end --------------------------------------------------------------------------------