├── main.sql ├── fxmanifest.lua ├── LICENSE ├── modules ├── server │ ├── command.lua │ ├── db.lua │ └── load.lua └── client │ ├── events.lua │ ├── spawn.lua │ └── functions.lua ├── configuration └── config.lua └── README.md /main.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS mprops ( 2 | ID INT AUTO_INCREMENT PRIMARY KEY, 3 | propid VARCHAR(50) NOT NULL, 4 | propname VARCHAR(100) NOT NULL, 5 | x FLOAT NOT NULL, 6 | y FLOAT NOT NULL, 7 | z FLOAT NOT NULL, 8 | freeze TINYINT(1) NOT NULL DEFAULT 0, 9 | colision TINYINT(1) NOT NULL DEFAULT 1, 10 | heading FLOAT NOT NULL, 11 | created TIMESTAMP DEFAULT CURRENT_TIMESTAMP 12 | ); 13 | -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | game 'gta5' 3 | lua54 'true' 4 | author 'M-DEVELOPMENT' 5 | description 'Prop Creator for FiveM' 6 | version '1.0.0' 7 | 8 | client_scripts { 9 | 'modules/client/events.lua', 10 | 'modules/client/functions.lua', 11 | 'modules/client/spawn.lua', 12 | } 13 | 14 | shared_scripts { 15 | '@ox_lib/init.lua', 16 | 'configuration/config.lua', 17 | } 18 | 19 | server_scripts { 20 | 'modules/server/db.lua', 21 | 'modules/server/command.lua', 22 | 'modules/server/load.lua', 23 | '@oxmysql/lib/MySQL.lua' 24 | } 25 | 26 | escrow_ignore { 27 | 'configuration/config.lua', 28 | } 29 | 30 | dependencies { 31 | 'oxmysql', 32 | 'ox_lib' 33 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 M-DEVELOPMENT 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 | -------------------------------------------------------------------------------- /modules/server/command.lua: -------------------------------------------------------------------------------- 1 | local COMMAND_CONFIG = { 2 | CREATE = { 3 | COMMAND = Configuration.Command, 4 | DESCRIPTION = Configuration.CommandDesc, 5 | ACCESS = Configuration.CommandAccess, 6 | EVENT = "m:propcreator:openmenu" 7 | }, 8 | DELETE = { 9 | COMMAND = Configuration.CommandDeleteProps, 10 | DESCRIPTION = Configuration.CommandDeletePropsDesc, 11 | ACCESS = Configuration.CommandDeletePropsAccess, 12 | EVENT = "m:propcreator:DeleteAllProps" 13 | } 14 | } 15 | 16 | for _, config in pairs(COMMAND_CONFIG) do 17 | lib.addCommand(config.COMMAND, { 18 | help = config.DESCRIPTION, 19 | restricted = config.ACCESS 20 | }, function(source) 21 | TriggerClientEvent(config.EVENT, source) 22 | end) 23 | end 24 | 25 | local SCRIPT_VERSION = "1.0.0" 26 | 27 | print([[ 28 | __ __ _____ ________ ________ _ ____ _____ __ __ ______ _ _ _______ 29 | | \/ | | __ \| ____\ \ / / ____| | / __ \| __ \| \/ | ____| \ | |__ __| 30 | | \ / |______| | | | |__ \ \ / /| |__ | | | | | | |__) | \ / | |__ | \| | | | 31 | | |\/| |______| | | | __| \ \/ / | __| | | | | | | ___/| |\/| | __| | . ` | | | 32 | | | | | | |__| | |____ \ / | |____| |___| |__| | | | | | | |____| |\ | | | 33 | |_| |_| |_____/|______| \/ |______|______\____/|_| |_| |_|______|_| \_| |_| 34 | ]]) 35 | 36 | print("Versión del script: " .. SCRIPT_VERSION) 37 | 38 | 39 | -------------------------------------------------------------------------------- /modules/server/db.lua: -------------------------------------------------------------------------------- 1 | 2 | RegisterNetEvent("m:propcreator:savePropPosition") 3 | AddEventHandler("m:propcreator:savePropPosition", function(model, x, y, z, heading, collision, frozen) 4 | local propid = tostring(math.random(100000, 999999)) 5 | 6 | local query = [[ 7 | INSERT INTO mprops (propid, propname, x, y, z, freeze, colision, heading) 8 | VALUES (?, ?, ?, ?, ?, ?, ?, ?) 9 | ]] 10 | 11 | MySQL.Async.execute(query, { 12 | propid, model, x, y, z, 13 | frozen and 1 or 0, 14 | collision and 1 or 0, 15 | heading 16 | }) 17 | end) 18 | 19 | 20 | RegisterNetEvent("m:propcreator:TakePropsInfo") 21 | AddEventHandler("m:propcreator:TakePropsInfo", function() 22 | local src = source 23 | 24 | local query = "SELECT * FROM mprops" 25 | 26 | MySQL.Async.fetchAll(query, {}, function(result) 27 | if result and #result > 0 then 28 | TriggerClientEvent("m:propcreator:ReceivePropsInfo", src, result) 29 | else 30 | TriggerClientEvent("m:propcreator:ReceivePropsInfo", src, {}) 31 | end 32 | end) 33 | end) 34 | 35 | RegisterNetEvent('m:propcreator:RemoveProp') 36 | AddEventHandler('m:propcreator:RemoveProp', function(propid) 37 | 38 | local query = "DELETE FROM mprops WHERE propid = @propid" 39 | 40 | MySQL.Async.execute(query, { ['@propid'] = propid }) 41 | 42 | TriggerClientEvent("m:propcreator:DeleteAllProps", -1) 43 | end) 44 | 45 | RegisterNetEvent('m:propcreator:RemoveAllProps') 46 | AddEventHandler('m:propcreator:RemoveAllProps', function() 47 | local query = "DELETE FROM mprops" 48 | 49 | MySQL.Async.execute(query, {}) 50 | 51 | TriggerClientEvent("m:propcreator:DeleteAllProps", -1) 52 | end) 53 | -------------------------------------------------------------------------------- /modules/server/load.lua: -------------------------------------------------------------------------------- 1 | local spawnedProps = {} 2 | 3 | function LoadPropsFromDB(event) 4 | 5 | if event == "creator" then 6 | spawnedProps = {} 7 | local query = "SELECT * FROM mprops" 8 | MySQL.Async.fetchAll(query, {}, function(props) 9 | if props and #props > 0 then 10 | for _, prop in ipairs(props) do 11 | spawnedProps[prop.propid] = prop 12 | end 13 | TriggerClientEvent("m:propcreator:SpawnProps", -1, spawnedProps) 14 | else 15 | print("^3[WARNING] No props found in the database.^0") 16 | end 17 | end) 18 | elseif event == "delete" then 19 | local src = source 20 | 21 | MySQL.Async.fetchAll("SELECT * FROM mprops", {}, function(result) 22 | TriggerClientEvent('m:propcreator:createremovemenu', src, result) 23 | end) 24 | else 25 | print("^1[ERROR] Invalid event or source in LoadPropsFromDB.^0") 26 | end 27 | end 28 | 29 | RegisterNetEvent("m:propcreator:RequestProps") 30 | AddEventHandler("m:propcreator:RequestProps", function(event) 31 | LoadPropsFromDB(event) 32 | end) 33 | 34 | 35 | 36 | 37 | CreateThread(function() 38 | local query = [[ 39 | SELECT COUNT(*) AS count 40 | FROM information_schema.tables 41 | WHERE table_schema = DATABASE() AND table_name = 'mprops' 42 | ]] 43 | 44 | MySQL.Async.fetchScalar(query, {}, function(result) 45 | if result and tonumber(result) > 0 then 46 | print("^2[INFO] ✅ Database check passed: 'mprops' table found.^0") 47 | else 48 | print("^1[ERROR] ❌ Database check failed: 'mprops' table is missing!^0") 49 | print("^1[ERROR] Please execute the following SQL query in your database:^0") 50 | print([[ 51 | CREATE TABLE IF NOT EXISTS `mprops` ( 52 | `id` INT AUTO_INCREMENT PRIMARY KEY, 53 | `propid` VARCHAR(50) NOT NULL, 54 | `propname` VARCHAR(100) NOT NULL, 55 | `x` FLOAT NOT NULL, 56 | `y` FLOAT NOT NULL, 57 | `z` FLOAT NOT NULL, 58 | `heading` FLOAT NOT NULL, 59 | `freeze` BOOLEAN NOT NULL, 60 | `colision` BOOLEAN NOT NULL 61 | ); 62 | ]]) 63 | end 64 | 65 | end) 66 | end) 67 | -------------------------------------------------------------------------------- /configuration/config.lua: -------------------------------------------------------------------------------- 1 | Configuration = { 2 | Debug = false, 3 | Framework = "QBCore", --- QBCore / esx 4 | 5 | Command = "propcreator", 6 | CommandAccess = "group.admin", 7 | CommandDesc = "Open Prop Creator Menu", 8 | 9 | CommandDeleteProps = "m:propcreator:deleteprops", 10 | CommandDeletePropsAccess = "group.admin", 11 | CommandDeletePropsDesc = "Delete all props created (Security)", 12 | 13 | 14 | DistanceLoad = 50.0, 15 | 16 | 17 | Translations = { 18 | MenuTitle = "Prop Creator Menu", 19 | CreateProp = "Create a Prop", 20 | CreatePropDesc = "Spawn a new prop at your current location", 21 | DeleteProps = "Delete Props", 22 | DeletePropsDesc = "Remove all props created", 23 | 24 | --- Remove Prop Menu translations --- 25 | RemovePropsMenuTitle = "Prop Removal Menu", 26 | RemoveSpecificProp = "Remove a Specific Prop", 27 | RemoveSpecificPropDesc = "Select and remove a single placed prop", 28 | RemoveAllProps = "Remove All Props", 29 | RemoveAllPropsDesc = "Deletes every placed prop from the world", 30 | 31 | PropDialogTitle = "Prop Creation", 32 | PropID = "Prop ID", 33 | PropIDDesc = "Enter the unique ID ex (prop_generator_03b).", 34 | PropName = "Prop Name", 35 | PropNameDesc = "Enter a prop name to sabe in to db (3-50 characters).", 36 | PropFreeze = "Freeze Prop?", 37 | PropFreezeDesc = "Should the prop be static and immovable?", 38 | PropColisions = "Collisions?", 39 | PropColisionsDesc = "Should the prop be able to move or interact with physics?", 40 | 41 | --- METADATA --- 42 | --- 43 | Coordinates = "Coordinates", 44 | PropIDName = "Prop ID", 45 | Heading = "Heading", 46 | Frozen = "Frozen", 47 | Collisions = "Collisions", 48 | 49 | 50 | NoPropsOnDB = "No props found in the database.", 51 | ConfirmDeleteHeader = "Confirm Deletion", 52 | ConfirmDeleteContent = "Are you sure you want to delete ALL props?\nThis action cannot be undone.", 53 | --- Controls UI --- 54 | ControlsUI = "[Q] - Move Up \n" .. 55 | "[E] - Move Down \n" .. 56 | "[ARROWS] - Move \n" .. 57 | "[Scroll Wheel] - Rotate \n" .. 58 | "[LALT] - Adjust Height \n" .. 59 | "[ESC] - Finish Editing \n" 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /modules/client/events.lua: -------------------------------------------------------------------------------- 1 | local EVENT_NAMES = { 2 | CREATE_REMOVE_MENU = 'm:propcreator:createremovemenu', 3 | REMOVE_PROP = 'm:propcreator:RemoveProp', 4 | REQUEST_PROPS = 'm:propcreator:RequestProps', 5 | OPEN_MENU = 'm:propcreator:openmenu', 6 | DELETE_ALL_PROPS = 'm:propcreator:DeleteAllProps' 7 | } 8 | 9 | local function CreateRemovePropsMenu(props) 10 | if not props or #props == 0 then 11 | lib.notify({ description = Configuration.Translations.NoPropsOnDB, type = 'error' }) 12 | return 13 | end 14 | 15 | local options = {} 16 | 17 | for _, prop in ipairs(props) do 18 | table.insert(options, { 19 | title = prop.propname, 20 | metadata = { 21 | { label = Configuration.Translations.Collisions, value = string.format("[%.2f, %.2f, %.2f]", prop.x, prop.y, prop.z) }, 22 | { label = Configuration.Translations.PropIDName, value = prop.propid }, 23 | { label = Configuration.Translations.Heading, value = prop.heading }, 24 | { label = Configuration.Translations.Frozen, value = prop.freeze and "Yes" or "No" }, 25 | { label = Configuration.Translations.Collisions, value = prop.colision and "Enabled" or "Disabled" } 26 | }, 27 | icon = 'box', 28 | onSelect = function() 29 | TriggerServerEvent(EVENT_NAMES.REMOVE_PROP, prop.propid) 30 | TriggerServerEvent(EVENT_NAMES.REQUEST_PROPS, "creator") 31 | end 32 | }) 33 | end 34 | 35 | lib.registerContext({ 36 | id = 'prop_remove_menu', 37 | title = Configuration.Translations.RemovePropsMenuTitle, 38 | options = options 39 | }) 40 | 41 | lib.showContext('prop_remove_menu') 42 | end 43 | 44 | RegisterNetEvent(EVENT_NAMES.CREATE_REMOVE_MENU, CreateRemovePropsMenu) 45 | 46 | RegisterNetEvent(EVENT_NAMES.OPEN_MENU, function() 47 | ShowPropCreatorMenu() 48 | end) 49 | 50 | if Configuration.Framework == "QBCore" then 51 | AddEventHandler('QBCore:Client:OnPlayerLoaded', function() 52 | TriggerServerEvent(EVENT_NAMES.REQUEST_PROPS, "creator") 53 | end) 54 | elseif Configuration.Framework == "ESX" then 55 | AddEventHandler('esx:playerLoaded', function() 56 | TriggerServerEvent(EVENT_NAMES.REQUEST_PROPS, "creator") 57 | end) 58 | end 59 | 60 | AddEventHandler("onResourceStop", function(resource) 61 | if resource == GetCurrentResourceName() then 62 | TriggerEvent(EVENT_NAMES.DELETE_ALL_PROPS) 63 | end 64 | end) 65 | -------------------------------------------------------------------------------- /modules/client/spawn.lua: -------------------------------------------------------------------------------- 1 | local placedProps = {} 2 | local propsData = {} 3 | 4 | RegisterNetEvent("m:propcreator:SpawnProps") 5 | AddEventHandler("m:propcreator:SpawnProps", function(props) 6 | for _, prop in pairs(props) do 7 | if not propsData[prop.propid] then 8 | propsData[prop.propid] = prop 9 | end 10 | end 11 | end) 12 | 13 | CreateThread(function() 14 | while true do 15 | local playerPed = PlayerPedId() 16 | local playerCoords = GetEntityCoords(playerPed) 17 | 18 | for propid, prop in pairs(propsData) do 19 | local distance = #(vector3(prop.x, prop.y, prop.z) - playerCoords) 20 | 21 | if distance < Configuration.DistanceLoad then 22 | if not placedProps[propid] then 23 | RequestModel(prop.propname) 24 | while not HasModelLoaded(prop.propname) do Wait(50) end 25 | 26 | local obj = CreateObjectNoOffset(prop.propname, prop.x, prop.y, prop.z, false, true, false) 27 | SetEntityHeading(obj, prop.heading) 28 | FreezeEntityPosition(obj, prop.freeze) 29 | SetEntityCollision(obj, prop.colision, true) 30 | SetEntityAlpha(obj, 0, false) 31 | placedProps[propid] = obj 32 | 33 | CreateThread(function() 34 | for alpha = 0, 255, 25 do 35 | if placedProps[propid] then 36 | SetEntityAlpha(placedProps[propid], alpha, false) 37 | Wait(50) 38 | end 39 | end 40 | end) 41 | end 42 | elseif placedProps[propid] then 43 | CreateThread(function() 44 | for alpha = 255, 0, -25 do 45 | if placedProps[propid] then 46 | SetEntityAlpha(placedProps[propid], alpha, false) 47 | Wait(50) 48 | end 49 | end 50 | 51 | if placedProps[propid] then 52 | DeleteEntity(placedProps[propid]) 53 | placedProps[propid] = nil 54 | end 55 | end) 56 | end 57 | end 58 | 59 | Wait(2000) 60 | end 61 | end) 62 | 63 | 64 | 65 | 66 | RegisterNetEvent("m:propcreator:DeleteAllProps") 67 | AddEventHandler("m:propcreator:DeleteAllProps", function() 68 | for _, obj in pairs(placedProps) do 69 | if DoesEntityExist(obj) then 70 | DeleteEntity(obj) 71 | end 72 | end 73 | placedProps = {} 74 | propsData = {} 75 | end) 76 | 77 | 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Prop Creator - Standalone FiveM Script 2 | 3 | ## Table of Contents 4 | 5 | - [Description](#description-) 6 | - [Features](#features-️) 7 | - [Installation](#installation-) 8 | - [Usage](#usage-) 9 | - [Available Events](#available-events-) 10 | - [Controls](#controls-) 11 | - [Optimization](#optimization-) 12 | - [License](#license-) 13 | 14 | ## Description 📜 15 | **Prop Creator** is a fully standalone script that allows the creation and management of props within the game world. With this script, administrators can dynamically generate, place, and remove objects through an intuitive interface with seamless database integration. 16 | 17 | ## Features 🛠️ 18 | ✅ **Real-time prop creation** with customizable position, rotation, and collision settings. 19 | ✅ **Database-backed prop management** for full persistence. 20 | ✅ **Prop removal menu**, with options to delete a specific prop or all at once. 21 | ✅ **Smooth spawning and despawning effects** to optimize performance and improve immersion. 22 | ✅ **Support for multiple notification systems**, including `ox_lib`. 23 | ✅ **Configurable permission system** to restrict command access. 24 | ✅ **Highly optimized** for efficient server performance. 25 | 26 | --- 27 | 28 | ## Installation 📥 29 | ### 1️⃣ **Download and add the script** 30 | Place the script folder in your server's `resources/` directory and rename it to **M-PropV2** 31 | 32 | ### 2️⃣ **Configure `server.cfg`** 33 | Add the following line to your `server.cfg`: 34 | ``` 35 | ensure M-PropV2 36 | ``` 37 | 38 | ### 3️⃣ **Set up the database** 39 | Run the following SQL query in your database to store props: 40 | ```sql 41 | CREATE TABLE IF NOT EXISTS `mprops` ( 42 | `id` INT AUTO_INCREMENT PRIMARY KEY, 43 | `propid` VARCHAR(50) NOT NULL, 44 | `propname` VARCHAR(100) NOT NULL, 45 | `x` FLOAT NOT NULL, 46 | `y` FLOAT NOT NULL, 47 | `z` FLOAT NOT NULL, 48 | `heading` FLOAT NOT NULL, 49 | `freeze` BOOLEAN NOT NULL, 50 | `colision` BOOLEAN NOT NULL 51 | ); 52 | ``` 53 | 54 | --- 55 | 56 | ## Usage 📌 57 | ### 🏗️ **Creating a Prop** 58 | - Use the `/propcreator` command to open the admin menu. 59 | - Select the **"Create a Prop"** option and enter the required details. 60 | - Place the prop in the world using movement controls. 61 | 62 | ### 🗑️ **Deleting Props** 63 | - Access the deletion menu via `/propcreator`. 64 | - Choose to remove a specific prop or delete all props from the server. 65 | 66 | ### 🎛️ **Configuring Permissions** 67 | Modify permissions in `config.lua`: 68 | ```lua 69 | Configuration = { 70 | Command = "propcreator", 71 | CommandAccess = "group.admin", 72 | } 73 | ``` 74 | 75 | --- 76 | 77 | ## Available Events 📡 78 | ### 🔹 **Client → Server** 79 | | Event | Description | 80 | |--------------------------------|-------------| 81 | | `m:propcreator:RequestProps` | Requests the list of stored props from the database. | 82 | | `m:propcreator:RemoveProp` | Deletes a specific prop by ID. | 83 | | `m:propcreator:RemoveAllProps` | Deletes all stored props. | 84 | 85 | ### 🔹 **Server → Client** 86 | | Event | Description | 87 | |----------------------------------|-------------| 88 | | `m:propcreator:SpawnProps` | Spawns props stored in the database. | 89 | | `m:propcreator:DeleteAllProps` | Removes all props from the game world. | 90 | | `m:propcreator:createremovemenu` | Displays the prop deletion menu. | 91 | 92 | --- 93 | 94 | ## Controls 🎮 95 | These are the controls for moving and adjusting props in the game world: 96 | ``` 97 | [Q] - Move Up 98 | [E] - Move Down 99 | [ARROWS] - Move 100 | [Scroll Wheel] - Rotate 101 | [LALT] - Adjust Height 102 | [ESC] - Finish Editing 103 | ``` 104 | 105 | --- 106 | 107 | ## Optimization 🚀 108 | This script is designed to be **lightweight and efficient**, using: 109 | - **Dynamic loading and unloading of props based on distance** to reduce server load. 110 | - **Well-structured event handling** to avoid unnecessary executions. 111 | - **Integration with `ox_lib`** for managing interfaces and notifications. 112 | 113 | --- 114 | 115 | ## Credits 👨‍💻 116 | **Developer:** *M-DEVELOPMENT* 117 | For support or suggestions, feel free to reach out! 🎉 118 | 119 | --- 120 | 121 | ## License 📜 122 | This script is open-source. You may modify and adapt it to your needs, but **reselling it is not allowed**. 123 | 124 | -------------------------------------------------------------------------------- /modules/client/functions.lua: -------------------------------------------------------------------------------- 1 | local loadedModels = {} 2 | 3 | function ShowPropCreatorMenu() 4 | lib.registerContext({ 5 | id = 'prop_creator_menu', 6 | title = Configuration.Translations.MenuTitle, 7 | options = { 8 | { 9 | title = Configuration.Translations.CreateProp, 10 | description = Configuration.Translations.CreatePropDesc, 11 | icon = 'box', 12 | onSelect = OpenPropInput, 13 | }, 14 | { 15 | title = Configuration.Translations.DeleteProps, 16 | description = Configuration.Translations.DeletePropsDesc, 17 | icon = 'trash', 18 | onSelect = ShowDeleteMenu, 19 | }, 20 | }, 21 | }) 22 | lib.showContext('prop_creator_menu') 23 | end 24 | 25 | function OpenPropInput() 26 | local input = lib.inputDialog(Configuration.Translations.PropDialogTitle, { 27 | { type = 'input', label = Configuration.Translations.PropID, description = Configuration.Translations.PropIDDesc, required = true, min = 3, max = 20 }, 28 | { type = 'input', label = Configuration.Translations.PropName, description = Configuration.Translations.PropNameDesc, required = true, min = 3, max = 50 }, 29 | { type = 'checkbox', label = Configuration.Translations.PropFreeze, description = Configuration.Translations.PropFreezeDesc, required = false }, 30 | { type = 'checkbox', label = Configuration.Translations.PropColisions, description = Configuration.Translations.PropColisionsDesc, required = false }, 31 | }) 32 | 33 | if input then StartPropPlacement(input) end 34 | end 35 | 36 | function ShowDeleteMenu() 37 | lib.registerContext({ 38 | id = 'prop_delete_menu', 39 | title = Configuration.Translations.RemovePropsMenuTitle, 40 | options = { 41 | { 42 | title = Configuration.Translations.RemoveSpecificProp, 43 | description = Configuration.Translations.RemoveSpecificPropDesc, 44 | icon = 'box', 45 | onSelect = function() 46 | TriggerServerEvent("m:propcreator:RequestProps", "delete") 47 | end, 48 | }, 49 | { 50 | title = Configuration.Translations.RemoveAllProps, 51 | description = Configuration.Translations.RemoveAllPropsDesc, 52 | icon = 'trash', 53 | onSelect = function() 54 | local confirm = lib.alertDialog({ 55 | header = Configuration.Translations.ConfirmDeleteHeader, 56 | content = Configuration.Translations.ConfirmDeleteContent, 57 | centered = true, 58 | cancel = true, 59 | }) 60 | 61 | if confirm then 62 | TriggerEvent("m:propcreator:DeleteAllProps") 63 | TriggerServerEvent("m:propcreator:RemoveAllProps") 64 | end 65 | end, 66 | }, 67 | }, 68 | }) 69 | lib.showContext('prop_delete_menu') 70 | end 71 | 72 | function StartPropPlacement(input) 73 | local modelName = input[1] 74 | 75 | if not loadedModels[modelName] then 76 | if not IsModelInCdimage(modelName) then 77 | return lib.notify({ description = "El modelo de prop no es válido.", type = 'error' }) 78 | end 79 | 80 | RequestModel(modelName) 81 | while not HasModelLoaded(modelName) do Wait(100) end 82 | loadedModels[modelName] = true 83 | end 84 | 85 | local playerPed = PlayerPedId() 86 | local forwardVector = GetEntityForwardVector(playerPed) 87 | local playerCoords = GetEntityCoords(playerPed) 88 | 89 | local propCoords = vector3(playerCoords.x + forwardVector.x * 1.5, playerCoords.y + forwardVector.y * 1.5, playerCoords.z) 90 | local prop = CreateObjectNoOffset(modelName, propCoords.x, propCoords.y, propCoords.z, false, false, false) 91 | 92 | SetEntityCollision(prop, true, true) 93 | SetEntityAlpha(prop, 200, false) 94 | FreezeEntityPosition(prop, true) 95 | 96 | local editing = true 97 | 98 | lib.showTextUI(Configuration.Translations.ControlsUI) 99 | 100 | Citizen.CreateThread(function() 101 | while DoesEntityExist(prop) and editing do 102 | Citizen.Wait(0) 103 | local x, y, z = table.unpack(GetEntityCoords(prop)) 104 | local heading = GetEntityHeading(prop) 105 | 106 | if Configuration.Debug then 107 | print(string.format("Posición del prop:\nX=%.2f\nY=%.2f\nZ=%.2f\nHeading=%.2f", x, y, z, heading)) 108 | end 109 | 110 | if IsControlPressed(0, 172) then SetEntityCoords(prop, x, y + 0.1, z) 111 | elseif IsControlPressed(0, 173) then SetEntityCoords(prop, x, y - 0.1, z) 112 | elseif IsControlPressed(0, 174) then SetEntityCoords(prop, x - 0.1, y, z) 113 | elseif IsControlPressed(0, 175) then SetEntityCoords(prop, x + 0.1, y, z) 114 | elseif IsControlPressed(0, 44) then SetEntityCoords(prop, x, y, z + 0.1) 115 | elseif IsControlPressed(0, 38) then SetEntityCoords(prop, x, y, z - 0.1) 116 | elseif IsControlPressed(0, 241) then SetEntityHeading(prop, heading + 1.5) 117 | elseif IsControlPressed(0, 242) then SetEntityHeading(prop, heading - 1.5) 118 | elseif IsControlPressed(0, 19) then 119 | local playerZ = GetEntityCoords(playerPed) 120 | SetEntityCoords(prop, x, y, playerZ.z - 1.0) 121 | elseif IsControlJustPressed(0, 322) then 122 | editing = false 123 | lib.hideTextUI() 124 | FreezeEntityPosition(prop, false) 125 | SetEntityCollision(prop, true, true) 126 | 127 | local finalCoords = GetEntityCoords(prop) 128 | local finalHeading = GetEntityHeading(prop) 129 | TriggerEvent("m:propcreator:DeleteAllProps") 130 | TriggerServerEvent("m:propcreator:savePropPosition", modelName, finalCoords.x, finalCoords.y, finalCoords.z, finalHeading, input[3], input[4]) 131 | Citizen.Wait(100) 132 | DeleteEntity(prop) 133 | Wait(1000) 134 | TriggerServerEvent("m:propcreator:RequestProps", "creator") 135 | end 136 | end 137 | end) 138 | end 139 | 140 | --------------------------------------------------------------------------------