2025-03-30 21:06:58 -05:00
Scaleform = Scaleform or Require ( "lib/scaleform/client/scaleform.lua" )
Utility = Utility or Require ( "lib/utility/client/utility.lua" )
Raycast = Raycast or Require ( "lib/raycast/client/raycast.lua" )
2025-06-04 15:29:21 -05:00
Language = Language or Require ( "modules/locales/shared.lua" )
2025-02-05 17:42:50 -06:00
PlaceableObject = PlaceableObject or {}
2025-02-12 08:58:36 -06:00
2025-06-04 15:29:21 -05:00
-- Local variables
local isPlacing = false
local currentEntity = nil
local currentMode = 'normal' -- 'normal' or 'movement'
local currentPromise = nil -- Store the current promise
2025-02-05 17:42:50 -06:00
2025-06-04 15:29:21 -05:00
-- Utility functions
local function eyetrace ( depth , disableSphere )
2025-02-05 17:42:50 -06:00
local screenX = GetDisabledControlNormal ( 0 , 239 )
local screenY = GetDisabledControlNormal ( 0 , 240 )
local world , normal = GetWorldCoordFromScreenCoord ( screenX , screenY )
local playerPos = GetEntityCoords ( PlayerPedId ())
local target = playerPos + normal * depth
if not disableSphere then
DrawSphere ( target.x , target.y , target.z , 0.5 , 255 , 0 , 0 , 0.5 )
end
return target
end
2025-06-04 15:29:21 -05:00
local function inBoundary ( pos , boundary )
if not boundary then return true end
2025-02-12 08:58:36 -06:00
local x , y , z = table.unpack ( pos )
local minX , minY , minZ = table.unpack ( boundary.min )
local maxX , maxY , maxZ = table.unpack ( boundary.max )
return x >= minX and x <= maxX and y >= minY and y <= maxY and z >= minZ and z <= maxZ
end
2025-03-24 18:06:52 -04:00
2025-06-04 15:29:21 -05:00
local function checkBoundaryAndMaterials ( entity , settings )
2025-02-12 08:58:36 -06:00
local inBounds = true
2025-06-04 15:29:21 -05:00
if settings.allowedMats or settings.boundary then
2025-02-12 08:58:36 -06:00
CreateThread ( function ()
2025-06-04 15:29:21 -05:00
while isPlacing do
local hit , _ , coords , _ , materialHash
if currentMode == 'movement' then
local entityCoords = GetEntityCoords ( entity )
local destination = entityCoords - vector3 ( 0 , 0 , settings.maxDepth or 10 )
hit , _ , coords , _ , materialHash = Raycast.ToCoords ( entityCoords , destination , 1 , 4 )
else
hit , _ , coords , _ , materialHash = Raycast.FromCamera ( 1 , 4 )
end
local validMaterial = not settings.allowedMats or settings.allowedMats [ materialHash ]
local validBoundary = inBoundary ( GetEntityCoords ( entity ), settings.boundary )
if hit and hit ~= 1 and validMaterial and validBoundary then
2025-02-12 08:58:36 -06:00
if not inBounds then
inBounds = true
SetEntityDrawOutlineColor ( 0 , 255 , 0 , 255 )
2025-06-04 15:29:21 -05:00
SetEntityDrawOutline ( entity , true )
2025-03-24 18:06:52 -04:00
end
2025-02-12 08:58:36 -06:00
elseif inBounds then
inBounds = false
2025-06-04 15:29:21 -05:00
SetEntityDrawOutline ( entity , false )
2025-02-12 08:58:36 -06:00
end
Wait ( 500 )
end
2025-06-04 15:29:21 -05:00
SetEntityDrawOutline ( entity , false )
2025-02-12 08:58:36 -06:00
end )
end
2025-06-04 15:29:21 -05:00
return function () return inBounds end
end
2025-02-05 17:42:50 -06:00
2025-06-04 15:29:21 -05:00
local function setupInstructionalButtons ( settings )
local buttons = {
{ type = "CLEAR_ALL" },
{ type = "SET_CLEAR_SPACE" , int = 200 },
}
2025-02-05 17:42:50 -06:00
2025-06-04 15:29:21 -05:00
-- Common buttons
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = settings.config ? . place_object ? . name or 'Place Object:' , keyIndex = settings.config ? . place_object ? . key or { 223 }, int = 5 })
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = settings.config ? . cancel_placement ? . name or 'Cancel Placement:' , keyIndex = settings.config ? . cancel_placement ? . key or { 222 }, int = 4 })
if currentMode == 'normal' then
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = settings.config ? . snap_to_ground ? . name or 'Snap to Ground:' , keyIndex = settings.config ? . snap_to_ground ? . key or { 19 }, int = 1 })
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = settings.config ? . rotate ? . name or 'Rotate:' , keyIndex = settings.config ? . rotate ? . key or { 14 , 15 }, int = 2 })
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = settings.config ? . distance ? . name or 'Distance:' , keyIndex = settings.config ? . distance ? . key or { 14 , 15 , 36 }, int = 3 })
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = settings.config ? . toggle_placement ? . name or 'Toggle Placement:' , keyIndex = settings.config ? . toggle_placement ? . key or { 199 }, int = 0 })
-- Add vertical controls if allowed
if settings.allowVertical then
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = 'Move Up/Down:' , keyIndex = { 85 , 48 }, int = 8 })
end
if settings.allowMovement then
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = settings.config ? . movement ? . name or 'Toggle Movement Mode:' , keyIndex = settings.config ? . movement ? . key or { 38 }, int = 6 })
end
elseif currentMode == 'movement' then
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = 'Move Forward/Back:' , keyIndex = { 32 , 33 }, int = 1 })
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = 'Move Left/Right:' , keyIndex = { 34 , 35 }, int = 2 })
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = 'Move Up/Down:' , keyIndex = { 85 , 48 }, int = 3 })
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = 'Rotate Left/Right:' , keyIndex = { 174 , 175 }, int = 4 })
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = 'Snap to Ground:' , keyIndex = { 19 }, int = 5 })
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = 'Confirm (Enter):' , keyIndex = { 191 }, int = 6 })
if settings.allowNormal then
table.insert ( buttons , { type = "SET_DATA_SLOT" , name = 'Normal Mode:' , keyIndex = { 38 }, int = 7 })
end
end
table.insert ( buttons , { type = "DRAW_INSTRUCTIONAL_BUTTONS" })
table.insert ( buttons , { type = "SET_BACKGROUND_COLOUR" })
return Scaleform.SetupInstructionalButtons ( buttons )
end
local function normalPlacementLoop ( entity , settings , getBoundsStatus )
-- Initialize or retrieve from settings to maintain state
settings.currentDepth = settings.currentDepth or settings.depth or 10.0
settings.currentHeading = settings.currentHeading or - GetEntityHeading ( PlayerPedId ())
settings.currentHeight = settings.currentHeight or 0.0 -- Add height tracking
settings.placeOnGround = settings.placeOnGround ~= nil and settings.placeOnGround or not settings.allowVertical -- Default based on allowVertical
settings.paused = settings.paused or false
local depth = settings.currentDepth
local heading = settings.currentHeading
local height = settings.currentHeight
local placeOnGround = settings.placeOnGround
local paused = settings.paused
local pos
-- Don't use while loop here, just process one frame
if not isPlacing or currentMode ~= 'normal' then
return
end
if paused then
if IsControlJustPressed ( 0 , 199 ) then -- home
paused = not paused
settings.paused = paused
end
return
end
-- Disable controls
DisableControlAction ( 0 , 24 , true ) -- Attack
DisableControlAction ( 0 , 25 , true ) -- Aim
DisableControlAction ( 0 , 36 , true ) -- INPUT_DUCK
-- Place object (left click)
if IsDisabledControlJustPressed ( 0 , 223 ) then
if not getBoundsStatus () then
print ( Language.Locale ( "placeable_object.cant_place_here" ))
else
pos = GetEntityCoords ( entity )
isPlacing = false
if currentPromise then
currentPromise : resolve ({
entity = entity ,
position = pos ,
heading = heading
})
currentPromise = nil
2025-02-05 17:42:50 -06:00
end
2025-06-04 15:29:21 -05:00
DeleteEntity ( entity )
return
end
end
2025-02-05 17:42:50 -06:00
2025-06-04 15:29:21 -05:00
-- Cancel (right click)
if IsDisabledControlJustPressed ( 0 , 25 ) then
isPlacing = false
if currentPromise then
currentPromise : resolve ( nil ) -- Return nil for cancel
currentPromise = nil
end
DeleteEntity ( entity )
return
end
-- Toggle snap to ground (Alt)
if IsControlJustPressed ( 0 , 19 ) then
if settings.allowVertical then
placeOnGround = not placeOnGround
settings.placeOnGround = placeOnGround
if placeOnGround then
height = 0.0 -- Reset height when snapping to ground
settings.currentHeight = height
2025-02-05 17:42:50 -06:00
end
2025-06-04 15:29:21 -05:00
else
-- Force snap to ground if vertical movement not allowed
PlaceObjectOnGroundProperly ( entity )
end
end
-- Switch to movement mode
if settings.allowMovement and IsControlJustPressed ( 0 , 38 ) then
currentMode = 'movement'
return
end
-- Vertical controls (only if allowVertical is enabled and not snapped to ground)
if settings.allowVertical and not placeOnGround then
if IsControlPressed ( 0 , 85 ) then -- Q - Move up
height = height + ( settings.heightStep or 0.1 )
settings.currentHeight = height
elseif IsControlPressed ( 0 , 48 ) then -- Z - Move down
height = height - ( settings.heightStep or 0.1 )
settings.currentHeight = height
end
end
-- Depth and rotation controls
if IsControlPressed ( 0 , 224 ) then -- left ctrl
if IsControlJustPressed ( 0 , 15 ) then -- D key
depth = math.min ( depth + settings.depthStep , settings.depthMax )
elseif IsControlJustPressed ( 0 , 14 ) then -- A key
depth = math.max ( depth - settings.depthStep , settings.depthMin )
end
else
if IsControlJustPressed ( 0 , 15 ) then -- D key
heading = ( heading + settings.rotationStep ) % 360
elseif IsControlJustPressed ( 0 , 14 ) then -- A key
heading = ( heading - settings.rotationStep ) % 360
end
end
2025-02-05 17:42:50 -06:00
2025-06-04 15:29:21 -05:00
-- Update position
pos = eyetrace ( depth , settings.disableSphere )
-- Add height offset if not snapping to ground
if settings.allowVertical and not placeOnGround then
pos = pos + vector3 ( 0 , 0 , height )
end
if entity then
SetEntityCoords ( entity , pos.x , pos.y , pos.z )
SetEntityHeading ( entity , heading )
if placeOnGround then
PlaceObjectOnGroundProperly ( entity )
pos = GetEntityCoords ( entity )
end
end
-- Store updated values back to settings
settings.currentDepth = depth
settings.currentHeading = heading
settings.currentHeight = height
settings.placeOnGround = placeOnGround
end
local function movementPlacementLoop ( entity , settings , getBoundsStatus )
-- Don't use while loop here, just process one frame
if not isPlacing or currentMode ~= 'movement' or not DoesEntityExist ( entity ) then
return
end
if IsEntityAPed ( entity ) then
SetEntityAlpha ( entity , 200 )
end
-- Disable movement controls to prevent player from moving
DisableControlAction ( 0 , 30 , true ) -- Move Left/Right
DisableControlAction ( 0 , 31 , true ) -- Move Forward/Back
DisableControlAction ( 0 , 36 , true ) -- INPUT_DUCK
DisableControlAction ( 0 , 21 , true ) -- Sprint
DisableControlAction ( 0 , 22 , true ) -- Jump
SetEntityCollision ( entity , false , false )
FreezeEntityPosition ( entity , false )
-- Get current entity position and rotation
local coords = GetEntityCoords ( entity )
local heading = GetEntityHeading ( entity )
local moved = false
-- Movement speed settings
local moveSpeed = 0.1
local fastMoveSpeed = 0.5
local rotateSpeed = 2.0
-- Check if shift is held for faster movement
local isFastMode = IsControlPressed ( 0 , 21 ) -- Left Shift
local currentMoveSpeed = isFastMode and fastMoveSpeed or moveSpeed
-- Get camera rotation for relative movement
local camRot = GetGameplayCamRot ( 2 )
local camHeading = math.rad ( camRot.z )
-- Calculate camera-relative directions
local camForward = vector3 ( - math.sin ( camHeading ), math.cos ( camHeading ), 0 )
local camRight = vector3 ( math.cos ( camHeading ), math.sin ( camHeading ), 0 )
-- WASD movement (relative to camera direction)
if IsControlPressed ( 0 , 32 ) then -- W - Move forward relative to camera
coords = coords + camForward * currentMoveSpeed
moved = true
elseif IsControlPressed ( 0 , 33 ) then -- S - Move backward relative to camera
coords = coords - camForward * currentMoveSpeed
moved = true
end
if IsControlPressed ( 0 , 34 ) then -- A - Move left relative to camera
coords = coords - camRight * currentMoveSpeed
moved = true
elseif IsControlPressed ( 0 , 35 ) then -- D - Move right relative to camera
coords = coords + camRight * currentMoveSpeed
moved = true
end
-- Up/Down movement (always world-relative)
if settings.allowVertical and IsControlPressed ( 0 , 85 ) then -- Q - Move up
coords = coords + vector3 ( 0 , 0 , currentMoveSpeed )
moved = true
elseif settings.allowVertical and IsControlPressed ( 0 , 48 ) then -- Z - Move down
coords = coords + vector3 ( 0 , 0 , - currentMoveSpeed )
moved = true
end
-- Rotation controls
if IsControlPressed ( 0 , 174 ) then -- Left Arrow - Rotate left
heading = heading - rotateSpeed
moved = true
elseif IsControlPressed ( 0 , 175 ) then -- Right Arrow - Rotate right
heading = heading + rotateSpeed
moved = true
end
-- Alternative rotation with mouse wheel
if IsControlJustPressed ( 0 , 241 ) then -- Mouse wheel up
heading = heading + 15.0
moved = true
elseif IsControlJustPressed ( 0 , 242 ) then -- Mouse wheel down
heading = heading - 15.0
moved = true
end
-- Apply movement if any occurred
if moved then
SetEntityCoords ( entity , coords.x , coords.y , coords.z , false , false , false , false )
SetEntityHeading ( entity , heading )
end
-- Confirm placement (Enter)
if IsControlJustPressed ( 0 , 191 ) then
if not getBoundsStatus () then
print ( Language.Locale ( "placeable_object.cant_place_here" ))
return
end
isPlacing = false
if currentPromise then
currentPromise : resolve ({
entity = entity ,
position = GetEntityCoords ( entity ),
heading = GetEntityHeading ( entity ),
rotation = GetEntityRotation ( entity )
})
currentPromise = nil
end
DeleteEntity ( entity )
return
end
-- Cancel placement (Backspace)
if IsControlJustPressed ( 0 , 202 ) then
isPlacing = false
if currentPromise then
currentPromise : resolve ( nil ) -- Return nil for cancel
currentPromise = nil
end
DeleteEntity ( entity )
return
end
-- Switch to normal mode (E)
if settings.allowNormal and IsControlJustPressed ( 0 , 38 ) then
currentMode = 'normal'
local playerPos = GetEntityCoords ( PlayerPedId ())
local entityPos = GetEntityCoords ( entity )
local depth = # ( playerPos - entityPos )
settings.currentDepth = depth
return
end
-- Snap to ground (Alt)
if IsControlJustPressed ( 0 , 19 ) then
PlaceObjectOnGroundProperly ( entity )
end
FreezeEntityPosition ( entity , false )
end
local function drawBoundaryBox ( boundary )
if not boundary then return end
local min = boundary.min
local max = boundary.max
-- Define the 8 corners of the box
local corners = {
vector3 ( min.x , min.y , min.z ), -- 1: bottom front left
vector3 ( max.x , min.y , min.z ), -- 2: bottom front right
vector3 ( max.x , max.y , min.z ), -- 3: bottom back right
vector3 ( min.x , max.y , min.z ), -- 4: bottom back left
vector3 ( min.x , min.y , max.z ), -- 5: top front left
vector3 ( max.x , min.y , max.z ), -- 6: top front right
vector3 ( max.x , max.y , max.z ), -- 7: top back right
vector3 ( min.x , max.y , max.z ), -- 8: top back left
}
-- Draw the box faces as triangles (2 triangles per face)
local alpha = 50 -- Semi-transparent
local r , g , b = 0 , 255 , 0 -- Green color
-- Bottom face (looking up from below)
DrawPoly ( corners [ 1 ]. x , corners [ 1 ]. y , corners [ 1 ]. z , corners [ 2 ]. x , corners [ 2 ]. y , corners [ 2 ]. z , corners [ 3 ]. x , corners [ 3 ]. y , corners [ 3 ]. z , r , g , b , alpha )
DrawPoly ( corners [ 1 ]. x , corners [ 1 ]. y , corners [ 1 ]. z , corners [ 3 ]. x , corners [ 3 ]. y , corners [ 3 ]. z , corners [ 4 ]. x , corners [ 4 ]. y , corners [ 4 ]. z , r , g , b , alpha )
-- Top face (looking down from above)
DrawPoly ( corners [ 5 ]. x , corners [ 5 ]. y , corners [ 5 ]. z , corners [ 7 ]. x , corners [ 7 ]. y , corners [ 7 ]. z , corners [ 6 ]. x , corners [ 6 ]. y , corners [ 6 ]. z , r , g , b , alpha )
DrawPoly ( corners [ 5 ]. x , corners [ 5 ]. y , corners [ 5 ]. z , corners [ 8 ]. x , corners [ 8 ]. y , corners [ 8 ]. z , corners [ 7 ]. x , corners [ 7 ]. y , corners [ 7 ]. z , r , g , b , alpha )
-- Front face (min Y)
DrawPoly ( corners [ 1 ]. x , corners [ 1 ]. y , corners [ 1 ]. z , corners [ 5 ]. x , corners [ 5 ]. y , corners [ 5 ]. z , corners [ 6 ]. x , corners [ 6 ]. y , corners [ 6 ]. z , r , g , b , alpha )
DrawPoly ( corners [ 1 ]. x , corners [ 1 ]. y , corners [ 1 ]. z , corners [ 6 ]. x , corners [ 6 ]. y , corners [ 6 ]. z , corners [ 2 ]. x , corners [ 2 ]. y , corners [ 2 ]. z , r , g , b , alpha )
-- Back face (max Y)
DrawPoly ( corners [ 4 ]. x , corners [ 4 ]. y , corners [ 4 ]. z , corners [ 7 ]. x , corners [ 7 ]. y , corners [ 7 ]. z , corners [ 8 ]. x , corners [ 8 ]. y , corners [ 8 ]. z , r , g , b , alpha )
DrawPoly ( corners [ 4 ]. x , corners [ 4 ]. y , corners [ 4 ]. z , corners [ 3 ]. x , corners [ 3 ]. y , corners [ 3 ]. z , corners [ 7 ]. x , corners [ 7 ]. y , corners [ 7 ]. z , r , g , b , alpha )
-- Left face (min X)
DrawPoly ( corners [ 1 ]. x , corners [ 1 ]. y , corners [ 1 ]. z , corners [ 4 ]. x , corners [ 4 ]. y , corners [ 4 ]. z , corners [ 8 ]. x , corners [ 8 ]. y , corners [ 8 ]. z , r , g , b , alpha )
DrawPoly ( corners [ 1 ]. x , corners [ 1 ]. y , corners [ 1 ]. z , corners [ 8 ]. x , corners [ 8 ]. y , corners [ 8 ]. z , corners [ 5 ]. x , corners [ 5 ]. y , corners [ 5 ]. z , r , g , b , alpha )
-- Right face (max X)
DrawPoly ( corners [ 2 ]. x , corners [ 2 ]. y , corners [ 2 ]. z , corners [ 6 ]. x , corners [ 6 ]. y , corners [ 6 ]. z , corners [ 7 ]. x , corners [ 7 ]. y , corners [ 7 ]. z , r , g , b , alpha )
DrawPoly ( corners [ 2 ]. x , corners [ 2 ]. y , corners [ 2 ]. z , corners [ 7 ]. x , corners [ 7 ]. y , corners [ 7 ]. z , corners [ 3 ]. x , corners [ 3 ]. y , corners [ 3 ]. z , r , g , b , alpha )
end
-- Main placement function - now uses proper promise pattern
function PlaceableObject . Create ( model , settings )
if isPlacing then
return nil -- Already placing
end
settings = settings or {}
-- Default settings
local config = {
depthMin = settings.depthMin or 2.0 ,
depthMax = settings.depthMax or 10.0 ,
rotationStep = settings.rotationStep or 15.0 ,
depthStep = settings.depthStep or 1.0 ,
heightStep = settings.heightStep or 0.1 , -- New height step setting
disableSphere = settings.useSphere and false or true ,
depth = settings.depth or 10.0 ,
allowMovement = settings.allowMovement ~= false , -- default true
allowNormal = settings.allowNormal ~= false , -- default true
allowVertical = settings.allowVertical ~= false , -- default true - allow vertical movement in normal mode
startMode = settings.startMode or 'normal' , -- 'normal' or 'movement'
allowedMats = settings.allowedMats ,
boundary = settings.boundary ,
config = settings.config or {},
maxDepth = settings.maxDepth or 10 ,
showInstructionalButtons = settings.showInstructionalButtons
}
-- Create the promise and store it globally
currentPromise = promise.new ()
isPlacing = true
currentMode = config.startMode
-- Create entity if model provided
if model then
local heading = - GetEntityHeading ( PlayerPedId ())
local pos = eyetrace ( config.depth , config.disableSphere )
local obj = Utility.CreateProp ( model , pos , vector3 ( 0 , 0 , heading ), nil )
currentEntity = obj
else
currentEntity = settings.entity
end
if not currentEntity then
isPlacing = false
if currentPromise then
currentPromise : resolve ( nil )
currentPromise = nil
end
return nil
end
-- Setup entity
SetEntityCollision ( currentEntity , false , false )
SetEntityAlpha ( currentEntity , 150 , false )
SetPedConfigFlag ( PlayerPedId (), 146 , true )
SetCanClimbOnEntity ( currentEntity , false )
SetEntityCompletelyDisableCollision ( currentEntity , true , false )
SetEntityNoCollisionEntity ( PlayerPedId (), currentEntity , false )
-- Setup boundary checking
local getBoundsStatus = checkBoundaryAndMaterials ( currentEntity , config )
-- Setup instructional buttons only if enabled
local scaleform = nil
if config.showInstructionalButtons then
scaleform = setupInstructionalButtons ( config )
end
-- Main loop
CreateThread ( function ()
local lastMode = currentMode
while isPlacing do
-- Update instructional buttons if mode changed and buttons are enabled
if config.showInstructionalButtons and ( not lastMode or lastMode ~= currentMode ) then
scaleform = setupInstructionalButtons ( config )
lastMode = currentMode
2025-02-05 17:42:50 -06:00
end
2025-06-04 15:29:21 -05:00
-- Draw instructional buttons only if enabled
if config.showInstructionalButtons and scaleform then
DrawScaleformMovieFullscreen ( scaleform , 255 , 255 , 255 , 255 , 0 )
end
2025-02-05 17:42:50 -06:00
2025-06-04 15:29:21 -05:00
-- Draw boundary box if defined
if config.boundary then
drawBoundaryBox ( config.boundary )
2025-02-05 17:42:50 -06:00
end
2025-06-04 15:29:21 -05:00
if currentMode == 'normal' and config.allowNormal then
normalPlacementLoop ( currentEntity , config , getBoundsStatus )
elseif currentMode == 'movement' and config.allowMovement then
movementPlacementLoop ( currentEntity , config , getBoundsStatus )
2025-02-05 17:42:50 -06:00
end
2025-06-04 15:29:21 -05:00
Wait ( 0 )
end
-- Cleanup
if DoesEntityExist ( currentEntity ) then
if IsEntityAPed ( currentEntity ) then
SetEntityAlpha ( currentEntity , 255 )
2025-02-05 17:42:50 -06:00
end
2025-06-04 15:29:21 -05:00
SetEntityDrawOutline ( currentEntity , false )
2025-02-05 17:42:50 -06:00
end
2025-06-04 15:29:21 -05:00
currentEntity = nil
2025-02-05 17:42:50 -06:00
end )
2025-06-04 15:29:21 -05:00
-- Return Citizen.Await on the promise
return Citizen.Await ( currentPromise )
2025-02-05 17:42:50 -06:00
end
2025-06-04 15:29:21 -05:00
-- Stop placement function
function PlaceableObject . Stop ()
if not isPlacing then return false end
isPlacing = false
if currentEntity and DoesEntityExist ( currentEntity ) then
DeleteEntity ( currentEntity )
end
return true
end
-- Get current status
function PlaceableObject . IsPlacing ()
return isPlacing
end
function PlaceableObject . GetCurrentEntity ()
return currentEntity
end
function PlaceableObject . GetCurrentMode ()
return currentMode
end
2025-03-24 18:06:52 -04:00
return PlaceableObject