Skip to content

Object Placement System for RedM โ€‹

A flexible and reusable object placement system for RedM, allowing for precise object preview and placement with rotation and height adjustment.

Features โ€‹

  • Preview-based placement system
  • Precise position control with intuitive movement
  • Multi-axis rotation support
  • Height adjustment
  • Customizable placement settings
  • Custom placement handlers
  • Built-in UI control display
  • Player state management during placement
  • Automatic resource cleanup

Usage โ€‹

Basic Usage โ€‹

lua
local Placer = exports.bln_lib:ObjPlacer()

-- Simple object placement with creation
RegisterCommand('placeobject', function()
    Placer.StartPlacement('p_beechers_ladder01x', {
        handlers = {
            afterPlace = function(placementData)
                -- Create the actual object after placement is confirmed
                local object = CreateObject(
                    placementData.model,
                    placementData.position.x,
                    placementData.position.y,
                    placementData.position.z,
                    true, true, false
                )
                
                -- Set rotation
                SetEntityRotation(object, 
                    placementData.rotation.x,
                    placementData.rotation.y,
                    placementData.rotation.z,
                    2, true
                )
            end
        }
    })
end)

Custom Settings โ€‹

lua
local settings = {
    moveSpeed = 0.05,      -- Movement speed
    rotationSpeed = 3.0,   -- Rotation speed
    heightSpeed = 0.05,    -- Height adjustment speed
    spawnDistance = 1.0,   -- Initial spawn distance from player
    spawnHeight = 0.0,     -- Initial spawn height offset
    placingAlpha = 128,    -- Preview transparency (0-255)
}

Placer.StartPlacement('p_beechers_ladder01x', settings)

Using Handlers โ€‹

The placement system supports four types of handlers:

  • beforePlace: Runs before placement confirmation, can prevent placement
  • onPlace: Runs during placement confirmation, can prevent placement
  • afterPlace: Runs after placement is confirmed, used for creating the actual object
  • onCancel: Runs when placement is cancelled or cleaning up
lua
local settings = {
    handlers = {
        beforePlace = function(placementData)
            -- Check conditions before placing
            local hasRequiredItems = exports.inventory:HasItems('required_item', 1)
            if not hasRequiredItems then
                Print("You need required items!")
                return false -- Prevents placement
            end
            return true -- Allows placement to continue
        end,
        
        onPlace = function(placementData)
            -- Run a minigame or skill check
            local success = exports.minigame:Start()
            return success -- Returns true/false to allow/prevent placement
        end,
        
        afterPlace = function(placementData)
            -- Create and setup the actual object
            local object = CreateObject(
                placementData.model,
                placementData.position,
                true, true, false
            )
            
            -- Additional setup...
            TriggerServerEvent('saveObject', placementData.position, placementData.rotation)
        end,
        
        onCancel = function(placementData)
            -- Handle cleanup or cancellation
            Print("Placement cancelled")
        end
    }
}

Placer.StartPlacement('p_beechers_ladder01x', settings)

Example: Placement with Animation โ€‹

lua
local settings = {
    handlers = {
        beforePlace = function(placementData)
            local ped = PlayerPedId()
            TaskStartScenarioInPlace(ped, "WORLD_HUMAN_HAMMER_WORK", 0, true)
            Wait(2000)
            ClearPedTasks(ped)
            return true
        end,
        afterPlace = function(placementData)
            -- Create the actual object here
            local object = CreateObject(placementData.model, placementData.position, true, true, false)
            SetEntityRotation(object, placementData.rotation.x, 0.0, placementData.rotation.z, 2, true)
        end
    }
}

Placer.StartPlacement('p_beechers_ladder01x', settings)

API Reference โ€‹

Exports โ€‹

ObjPlacer โ€‹

Returns the placement handler with the following methods:

StartPlacement(objectModel, settings) โ€‹

Starts the placement preview mode for the specified object.

  • objectModel: String - The model name of the object to place
  • settings: Table (optional) - Custom settings and handlers
CancelPlacement() โ€‹

Cancels the current placement and cleans up.

IsPlacing() โ€‹

Checks if placement mode is active.

  • Returns: boolean
GetPreviewData() โ€‹

Gets the current preview object data.

  • Returns: Table containing model, position, and rotation

Handler Data โ€‹

All handlers receive a placementData table containing:

lua
{
    model = hash,      -- Model hash of the object
    position = vector3, -- Final position
    rotation = vector3  -- Final rotation
}

Notes โ€‹

  • The system uses a preview object during placement
  • Preview objects are semi-transparent and non-colliding
  • Actual object creation is handled in the afterPlace handler
  • Player movement is restricted during placement
  • Resource automatically cleans up on stop
  • Multiple handlers can be used together
  • Handler return values control placement flow

Example Implementation โ€‹

Here's a complete example of a ladder placement system:

lua
local Placer = exports.bln_lib:ObjPlacer()
local ladderEntity = nil

local Settings = {
    moveSpeed = 0.05,
    rotationSpeed = 3.0,
    heightSpeed = 0.05,
    spawnDistance = 1.0,
    spawnHeight = 0.0,
    placingAlpha = 128,
    handlers = {
        afterPlace = function(placementData)
            -- Create the actual ladder
            local object = CreateObject(
                placementData.model,
                placementData.position.x,
                placementData.position.y,
                placementData.position.z,
                true, true, false
            )

            SetEntityRotation(object, 
                placementData.rotation.x,
                placementData.rotation.y,
                placementData.rotation.z,
                2, true
            )

            ladderEntity = object
            FreezeEntityPosition(object, true)
        end,
        onCancel = function(placementData)
            if ladderEntity then
                DeleteObject(ladderEntity)
                ladderEntity = nil
            end
        end
    }
}

RegisterCommand('ladder', function()
    if ladderEntity or Placer.IsPlacing() then
        if ladderEntity then
            DeleteObject(ladderEntity)
            ladderEntity = nil
        end
        Placer.CancelPlacement()
        return
    end

    Placer.StartPlacement('p_beechers_ladder01x', Settings)
end)